Add filter in podcast detail page.
This commit is contained in:
parent
d9bb4cf987
commit
c183683101
|
@ -10,6 +10,8 @@ import '../type/episodebrief.dart';
|
||||||
import '../webfeed/webfeed.dart';
|
import '../webfeed/webfeed.dart';
|
||||||
import '../type/sub_history.dart';
|
import '../type/sub_history.dart';
|
||||||
|
|
||||||
|
enum Filter { downloaded, liked, search, all }
|
||||||
|
|
||||||
class DBHelper {
|
class DBHelper {
|
||||||
static Database _db;
|
static Database _db;
|
||||||
Future<Database> get database async {
|
Future<Database> get database async {
|
||||||
|
@ -491,7 +493,6 @@ class DBHelper {
|
||||||
var feed = RssFeed.parse(response.data);
|
var feed = RssFeed.parse(response.data);
|
||||||
String url, description;
|
String url, description;
|
||||||
feed.items.removeWhere((item) => item == null);
|
feed.items.removeWhere((item) => item == null);
|
||||||
int result = feed.items.length;
|
|
||||||
|
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
int count = Sqflite.firstIntValue(await dbClient.rawQuery(
|
int count = Sqflite.firstIntValue(await dbClient.rawQuery(
|
||||||
|
@ -556,66 +557,117 @@ class DBHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> getRssItem(String id, int i, bool reverse) async {
|
Future<List<EpisodeBrief>> getRssItem(String id, int count, bool reverse,
|
||||||
|
{Filter filter = Filter.all, String query = ''}) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = [];
|
List<EpisodeBrief> episodes = [];
|
||||||
if (i == -1) {
|
List<Map> list = [];
|
||||||
List<Map> list = await dbClient
|
if (count == -1) {
|
||||||
|
switch (filter) {
|
||||||
|
case Filter.all:
|
||||||
|
list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
E.downloaded, P.primaryColor , E.media_id, E.is_new, P.skip_seconds
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE P.id = ? ORDER BY E.milliseconds ASC""", [id]);
|
WHERE P.id = ? ORDER BY E.milliseconds ASC""", [id]);
|
||||||
for (var i in list) {
|
break;
|
||||||
episodes.add(EpisodeBrief(
|
case Filter.liked:
|
||||||
i['title'],
|
list = await dbClient
|
||||||
i['enclosure_url'],
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
i['enclosure_length'],
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
i['milliseconds'],
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
i['feedTitle'],
|
WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds ASC""", [id]);
|
||||||
i['primaryColor'],
|
break;
|
||||||
i['liked'],
|
case Filter.downloaded:
|
||||||
i['downloaded'],
|
list = await dbClient.rawQuery(
|
||||||
i['duration'],
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
i['explicit'],
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
i['imagePath'],
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
i['media_id'],
|
WHERE P.id = ? AND E.media_id != E.enclosure_url ORDER BY E.milliseconds ASC""",
|
||||||
i['is_new'],
|
[id]);
|
||||||
i['skip_seconds']));
|
break;
|
||||||
|
case Filter.search:
|
||||||
|
list = await dbClient.rawQuery(
|
||||||
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds ASC""",
|
||||||
|
[id, '%$query%']);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
return episodes;
|
|
||||||
} else if (reverse) {
|
} else if (reverse) {
|
||||||
List<Map> list = await dbClient
|
switch (filter) {
|
||||||
|
case Filter.all:
|
||||||
|
list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
E.downloaded, P.primaryColor , E.media_id, E.is_new, P.skip_seconds
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
WHERE P.id = ? ORDER BY E.milliseconds ASC LIMIT ?""", [id, count]);
|
||||||
WHERE P.id = ? ORDER BY E.milliseconds ASC LIMIT ?""", [id, i]);
|
break;
|
||||||
for (var i in list) {
|
case Filter.liked:
|
||||||
episodes.add(EpisodeBrief(
|
list = await dbClient.rawQuery(
|
||||||
i['title'],
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
i['enclosure_url'],
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
i['enclosure_length'],
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
i['milliseconds'],
|
WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds ASC LIMIT ?""",
|
||||||
i['feedTitle'],
|
[id, count]);
|
||||||
i['primaryColor'],
|
break;
|
||||||
i['liked'],
|
case Filter.downloaded:
|
||||||
i['downloaded'],
|
list = await dbClient.rawQuery(
|
||||||
i['duration'],
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
i['explicit'],
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
i['imagePath'],
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
i['media_id'],
|
WHERE P.id = ? AND E.enclosure_url != E.media_id ORDER BY E.milliseconds ASC LIMIT ?""",
|
||||||
i['is_new'],
|
[id, count]);
|
||||||
i['skip_seconds']));
|
break;
|
||||||
|
case Filter.search:
|
||||||
|
list = await dbClient.rawQuery(
|
||||||
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds ASC LIMIT ?""",
|
||||||
|
[id, '%$query%', count]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
return episodes;
|
|
||||||
} else {
|
} else {
|
||||||
List<Map> list = await dbClient
|
switch (filter) {
|
||||||
|
case Filter.all:
|
||||||
|
list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
E.downloaded, P.primaryColor , E.media_id, E.is_new, P.skip_seconds
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
WHERE P.id = ? ORDER BY E.milliseconds DESC LIMIT ?""", [id, count]);
|
||||||
WHERE P.id = ? ORDER BY E.milliseconds DESC LIMIT ?""", [id, i]);
|
break;
|
||||||
|
case Filter.liked:
|
||||||
|
list = await dbClient.rawQuery(
|
||||||
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds DESC LIMIT ?""",
|
||||||
|
[id, count]);
|
||||||
|
break;
|
||||||
|
case Filter.downloaded:
|
||||||
|
list = await dbClient.rawQuery(
|
||||||
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE P.id = ? AND E.enclosure_url != E.media_id ORDER BY E.milliseconds DESC LIMIT ?""",
|
||||||
|
[id, count]);
|
||||||
|
break;
|
||||||
|
case Filter.search:
|
||||||
|
list = await dbClient.rawQuery(
|
||||||
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
|
E.milliseconds, P.imagePath, P.title as feedTitle, E.duration, E.explicit,
|
||||||
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
|
WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds DESC LIMIT ?""",
|
||||||
|
[id, '%$query%', count]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (list.isNotEmpty)
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
episodes.add(EpisodeBrief(
|
episodes.add(EpisodeBrief(
|
||||||
i['title'],
|
i['title'],
|
||||||
|
@ -624,18 +676,13 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feedTitle'],
|
i['feedTitle'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> getNewEpisodes(String id) async {
|
Future<List<EpisodeBrief>> getNewEpisodes(String id) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
|
@ -644,17 +691,15 @@ class DBHelper {
|
||||||
if (id == 'all')
|
if (id == 'all')
|
||||||
list = await dbClient.rawQuery(
|
list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE E.is_new = 1 AND E.downloaded = 'ND' AND P.auto_download = 1 ORDER BY E.milliseconds ASC""",
|
WHERE E.is_new = 1 AND E.downloaded = 'ND' AND P.auto_download = 1 ORDER BY E.milliseconds ASC""",
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
list = await dbClient.rawQuery(
|
list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE E.is_new = 1 AND E.downloaded = 'ND' AND E.feed_id = ? ORDER BY E.milliseconds ASC""",
|
WHERE E.is_new = 1 AND E.downloaded = 'ND' AND E.feed_id = ? ORDER BY E.milliseconds ASC""",
|
||||||
[id]);
|
[id]);
|
||||||
if (list.isNotEmpty)
|
if (list.isNotEmpty)
|
||||||
|
@ -666,14 +711,10 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
@ -683,9 +724,8 @@ class DBHelper {
|
||||||
List<EpisodeBrief> episodes = [];
|
List<EpisodeBrief> episodes = [];
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
where E.feed_id = ? ORDER BY E.milliseconds DESC LIMIT 2""", [id]);
|
where E.feed_id = ? ORDER BY E.milliseconds DESC LIMIT 2""", [id]);
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
episodes.add(EpisodeBrief(
|
episodes.add(EpisodeBrief(
|
||||||
|
@ -695,56 +735,21 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Future<EpisodeBrief> getRssItemDownload(String url) async {
|
|
||||||
// var dbClient = await database;
|
|
||||||
// EpisodeBrief episode;
|
|
||||||
// List<Map> list = await dbClient.rawQuery(
|
|
||||||
// """SELECT E.title, E.enclosure_url, E.enclosure_length,
|
|
||||||
// E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, E.liked,
|
|
||||||
// E.downloaded, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
|
||||||
// FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
// where E.enclosure_url = ? ORDER BY E.milliseconds DESC LIMIT 3""",
|
|
||||||
// [url]);
|
|
||||||
|
|
||||||
// if (list != null)
|
|
||||||
// episode = EpisodeBrief(
|
|
||||||
// list.first['title'],
|
|
||||||
// list.first['enclosure_url'],
|
|
||||||
// list.first['enclosure_length'],
|
|
||||||
// list.first['milliseconds'],
|
|
||||||
// list.first['feed_title'],
|
|
||||||
// list.first['primaryColor'],
|
|
||||||
// list.first['liked'],
|
|
||||||
// list.first['downloaded'],
|
|
||||||
// list.first['duration'],
|
|
||||||
// list.first['explicit'],
|
|
||||||
// list.first['imagePath'],
|
|
||||||
// list.first['media_id'],
|
|
||||||
// list.first['is_new'],
|
|
||||||
// list.first['skip_seconds']);
|
|
||||||
// return episode;
|
|
||||||
//}
|
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> getRecentRssItem(int top) async {
|
Future<List<EpisodeBrief>> getRecentRssItem(int top) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = [];
|
List<EpisodeBrief> episodes = [];
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
ORDER BY E.milliseconds DESC LIMIT ? """, [top]);
|
ORDER BY E.milliseconds DESC LIMIT ? """, [top]);
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
episodes.add(EpisodeBrief(
|
episodes.add(EpisodeBrief(
|
||||||
|
@ -754,14 +759,10 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
@ -774,9 +775,8 @@ class DBHelper {
|
||||||
List<String> s = group.map<String>((e) => "'$e'").toList();
|
List<String> s = group.map<String>((e) => "'$e'").toList();
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE P.id in (${s.join(',')})
|
WHERE P.id in (${s.join(',')})
|
||||||
ORDER BY E.milliseconds DESC LIMIT ? """, [top]);
|
ORDER BY E.milliseconds DESC LIMIT ? """, [top]);
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
|
@ -787,14 +787,10 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
|
@ -805,9 +801,8 @@ class DBHelper {
|
||||||
List<EpisodeBrief> episodes = [];
|
List<EpisodeBrief> episodes = [];
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE is_new = 1 ORDER BY E.milliseconds DESC """,
|
WHERE is_new = 1 ORDER BY E.milliseconds DESC """,
|
||||||
);
|
);
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
|
@ -818,56 +813,21 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Future<List<EpisodeBrief>> getNewRssItem(String id) async {
|
|
||||||
// var dbClient = await database;
|
|
||||||
// List<EpisodeBrief> episodes = [];
|
|
||||||
// List<Map> list = await dbClient.rawQuery(
|
|
||||||
// """SELECT E.title, E.enclosure_url, E.enclosure_length,
|
|
||||||
// E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
|
||||||
// E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
|
||||||
// FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
// WHERE is_new = 1 AND downloaded != 'ND' AND P.id = ?ORDER BY E.milliseconds DESC """,
|
|
||||||
// [id],
|
|
||||||
// );
|
|
||||||
// for (int x = 0; x < list.length; x++) {
|
|
||||||
// episodes.add(EpisodeBrief(
|
|
||||||
// list[x]['title'],
|
|
||||||
// list[x]['enclosure_url'],
|
|
||||||
// list[x]['enclosure_length'],
|
|
||||||
// list[x]['milliseconds'],
|
|
||||||
// list[x]['feed_title'],
|
|
||||||
// list[x]['primaryColor'],
|
|
||||||
// list[x]['liked'],
|
|
||||||
// list[x]['downloaded'],
|
|
||||||
// list[x]['duration'],
|
|
||||||
// list[x]['explicit'],
|
|
||||||
// list[x]['imagePath'],
|
|
||||||
// list[x]['media_id'],
|
|
||||||
// list[x]['is_new'],
|
|
||||||
// list[x]['skip_seconds']));
|
|
||||||
// }
|
|
||||||
// return episodes;
|
|
||||||
//}
|
|
||||||
Future<List<EpisodeBrief>> getOutdatedEpisode(int deadline) async {
|
Future<List<EpisodeBrief>> getOutdatedEpisode(int deadline) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<EpisodeBrief> episodes = [];
|
List<EpisodeBrief> episodes = [];
|
||||||
List<Map> list = await dbClient
|
List<Map> list = await dbClient
|
||||||
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
.rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE E.download_date < ? AND E.enclosure_url != E.media_id
|
WHERE E.download_date < ? AND E.enclosure_url != E.media_id
|
||||||
ORDER BY E.milliseconds DESC""", [deadline]);
|
ORDER BY E.milliseconds DESC""", [deadline]);
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
|
@ -878,14 +838,10 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
@ -898,9 +854,8 @@ class DBHelper {
|
||||||
if (mode == 0)
|
if (mode == 0)
|
||||||
list = await dbClient.rawQuery(
|
list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE E.enclosure_url != E.media_id
|
WHERE E.enclosure_url != E.media_id
|
||||||
ORDER BY E.download_date DESC""",
|
ORDER BY E.download_date DESC""",
|
||||||
);
|
);
|
||||||
|
@ -908,9 +863,8 @@ class DBHelper {
|
||||||
else if (mode == 1)
|
else if (mode == 1)
|
||||||
list = await dbClient.rawQuery(
|
list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE E.enclosure_url != E.media_id
|
WHERE E.enclosure_url != E.media_id
|
||||||
ORDER BY E.download_date ASC""",
|
ORDER BY E.download_date ASC""",
|
||||||
);
|
);
|
||||||
|
@ -918,9 +872,8 @@ class DBHelper {
|
||||||
else if (mode == 2)
|
else if (mode == 2)
|
||||||
list = await dbClient.rawQuery(
|
list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE E.enclosure_url != E.media_id
|
WHERE E.enclosure_url != E.media_id
|
||||||
ORDER BY E.enclosure_length DESC""",
|
ORDER BY E.enclosure_length DESC""",
|
||||||
);
|
);
|
||||||
|
@ -933,14 +886,9 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds'],
|
|
||||||
downloadDate: i['download_date']),
|
downloadDate: i['download_date']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -961,9 +909,8 @@ class DBHelper {
|
||||||
List<String> s = group.map<String>((e) => "'$e'").toList();
|
List<String> s = group.map<String>((e) => "'$e'").toList();
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
E.milliseconds, P.title as feed_title, E.duration, E.explicit,
|
||||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
P.imagePath, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
|
||||||
WHERE P.id in (${s.join(',')}) AND is_new = 1
|
WHERE P.id in (${s.join(',')}) AND is_new = 1
|
||||||
ORDER BY E.milliseconds DESC""",
|
ORDER BY E.milliseconds DESC""",
|
||||||
);
|
);
|
||||||
|
@ -975,14 +922,10 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
|
@ -1014,8 +957,8 @@ class DBHelper {
|
||||||
if (sortBy == 0) {
|
if (sortBy == 0) {
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
||||||
P.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
P.title as feed_title, E.duration, E.explicit, P.primaryColor
|
||||||
P.primaryColor, E.media_id, E.is_new, P.skip_seconds FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE E.liked = 1 ORDER BY E.milliseconds DESC LIMIT ?""", [i]);
|
WHERE E.liked = 1 ORDER BY E.milliseconds DESC LIMIT ?""", [i]);
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
episodes.add(EpisodeBrief(
|
episodes.add(EpisodeBrief(
|
||||||
|
@ -1025,20 +968,16 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
||||||
P.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
P.title as feed_title, E.duration, E.explicit, P.primaryColor
|
||||||
P.primaryColor, E.media_id, E.is_new, P.skip_seconds FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE E.liked = 1 ORDER BY E.liked_date DESC LIMIT ?""", [i]);
|
WHERE E.liked = 1 ORDER BY E.liked_date DESC LIMIT ?""", [i]);
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
episodes.add(EpisodeBrief(
|
episodes.add(EpisodeBrief(
|
||||||
|
@ -1048,14 +987,10 @@ class DBHelper {
|
||||||
i['milliseconds'],
|
i['milliseconds'],
|
||||||
i['feed_title'],
|
i['feed_title'],
|
||||||
i['primaryColor'],
|
i['primaryColor'],
|
||||||
i['liked'],
|
|
||||||
i['downloaded'],
|
|
||||||
i['duration'],
|
i['duration'],
|
||||||
i['explicit'],
|
i['explicit'],
|
||||||
i['imagePath'],
|
i['imagePath'],
|
||||||
i['media_id'],
|
));
|
||||||
i['is_new'],
|
|
||||||
i['skip_seconds']));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
|
@ -1150,8 +1085,8 @@ class DBHelper {
|
||||||
EpisodeBrief episode;
|
EpisodeBrief episode;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
||||||
P.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
P.title as feed_title, E.duration, E.explicit, P.skip_seconds,
|
||||||
P.primaryColor, E.media_id, E.is_new, P.skip_seconds FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE E.enclosure_url = ?""", [url]);
|
WHERE E.enclosure_url = ?""", [url]);
|
||||||
if (list.isEmpty) {
|
if (list.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1163,14 +1098,11 @@ class DBHelper {
|
||||||
list.first['milliseconds'],
|
list.first['milliseconds'],
|
||||||
list.first['feed_title'],
|
list.first['feed_title'],
|
||||||
list.first['primaryColor'],
|
list.first['primaryColor'],
|
||||||
list.first['liked'],
|
|
||||||
list.first['downloaded'],
|
|
||||||
list.first['duration'],
|
list.first['duration'],
|
||||||
list.first['explicit'],
|
list.first['explicit'],
|
||||||
list.first['imagePath'],
|
list.first['imagePath'],
|
||||||
list.first['media_id'],
|
mediaId: list.first['media_id'],
|
||||||
list.first['is_new'],
|
skipSeconds: list.first['skip_seconds']);
|
||||||
list.first['skip_seconds']);
|
|
||||||
return episode;
|
return episode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1180,8 +1112,8 @@ class DBHelper {
|
||||||
EpisodeBrief episode;
|
EpisodeBrief episode;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
||||||
P.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
P.title as feed_title, E.duration, E.explicit, P.skip_seconds,
|
||||||
P.primaryColor, E.media_id, E.is_new, P.skip_seconds FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
P.primaryColor, E.media_id FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||||
WHERE E.media_id = ?""", [id]);
|
WHERE E.media_id = ?""", [id]);
|
||||||
if (list.isEmpty)
|
if (list.isEmpty)
|
||||||
return null;
|
return null;
|
||||||
|
@ -1193,14 +1125,11 @@ class DBHelper {
|
||||||
list.first['milliseconds'],
|
list.first['milliseconds'],
|
||||||
list.first['feed_title'],
|
list.first['feed_title'],
|
||||||
list.first['primaryColor'],
|
list.first['primaryColor'],
|
||||||
list.first['liked'],
|
|
||||||
list.first['downloaded'],
|
|
||||||
list.first['duration'],
|
list.first['duration'],
|
||||||
list.first['explicit'],
|
list.first['explicit'],
|
||||||
list.first['imagePath'],
|
list.first['imagePath'],
|
||||||
list.first['media_id'],
|
mediaId: list.first['media_id'],
|
||||||
list.first['is_new'],
|
skipSeconds: list.first['skip_seconds']);
|
||||||
list.first['skip_seconds']);
|
|
||||||
return episode;
|
return episode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,46 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
/// Default layout.
|
/// Default layout.
|
||||||
Layout _layout;
|
Layout _layout;
|
||||||
|
|
||||||
bool _scroll;
|
/// If true, stop grid load animation.
|
||||||
|
bool _scroll = false;
|
||||||
|
|
||||||
|
double _topHeight = 0;
|
||||||
|
|
||||||
|
ScrollController _controller;
|
||||||
|
|
||||||
|
/// Episodes num load first time.
|
||||||
|
int _top = 96;
|
||||||
|
|
||||||
|
/// Load more episodes when scroll to bottom.
|
||||||
|
bool _loadMore = false;
|
||||||
|
|
||||||
|
/// Change sort by.
|
||||||
|
bool _reverse = false;
|
||||||
|
|
||||||
|
/// Filter type.
|
||||||
|
Filter _filter = Filter.all;
|
||||||
|
|
||||||
|
/// Query string
|
||||||
|
String _query = '';
|
||||||
|
|
||||||
|
///Hide listened.
|
||||||
|
bool _hideListened = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadMore = false;
|
||||||
|
_reverse = false;
|
||||||
|
_controller = ScrollController();
|
||||||
|
_scroll = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Future _updateRssItem(BuildContext context, PodcastLocal podcastLocal) async {
|
Future _updateRssItem(BuildContext context, PodcastLocal podcastLocal) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
final result = await dbHelper.updatePodcastRss(podcastLocal);
|
final result = await dbHelper.updatePodcastRss(podcastLocal);
|
||||||
|
@ -99,15 +138,16 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
if (mounted) setState(() {});
|
if (mounted) setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<EpisodeBrief>> _getRssItem(
|
Future<List<EpisodeBrief>> _getRssItem(PodcastLocal podcastLocal,
|
||||||
PodcastLocal podcastLocal, int i, bool reverse) async {
|
{int count, bool reverse, Filter filter, String query}) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
|
List<EpisodeBrief> episodes = [];
|
||||||
_episodeCount = await dbHelper.getPodcastCounts(podcastLocal.id);
|
_episodeCount = await dbHelper.getPodcastCounts(podcastLocal.id);
|
||||||
KeyValueStorage storage = KeyValueStorage(podcastLayoutKey);
|
KeyValueStorage storage = KeyValueStorage(podcastLayoutKey);
|
||||||
int index = await storage.getInt(defaultValue: 1);
|
int index = await storage.getInt(defaultValue: 1);
|
||||||
if (_layout == null) _layout = Layout.values[index];
|
if (_layout == null) _layout = Layout.values[index];
|
||||||
List<EpisodeBrief> episodes =
|
episodes = await dbHelper.getRssItem(podcastLocal.id, count, reverse,
|
||||||
await dbHelper.getRssItem(podcastLocal.id, i, reverse);
|
filter: filter, query: query);
|
||||||
if (podcastLocal.provider.contains('fireside')) {
|
if (podcastLocal.provider.contains('fireside')) {
|
||||||
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
|
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
|
||||||
await data.getData();
|
await data.getData();
|
||||||
|
@ -117,17 +157,6 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
_launchUrl(String url) async {
|
|
||||||
if (await canLaunch(url)) {
|
|
||||||
await launch(url);
|
|
||||||
} else {
|
|
||||||
Fluttertoast.showToast(
|
|
||||||
msg: '$url Invalid Link',
|
|
||||||
gravity: ToastGravity.TOP,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_markListened(String podcastId) async {
|
_markListened(String podcastId) async {
|
||||||
DBHelper dbHelper = DBHelper();
|
DBHelper dbHelper = DBHelper();
|
||||||
List<EpisodeBrief> episodes =
|
List<EpisodeBrief> episodes =
|
||||||
|
@ -234,6 +263,24 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_customPopupMenu(
|
||||||
|
{Widget child,
|
||||||
|
String tooltip,
|
||||||
|
List<PopupMenuEntry<int>> itemBuilder,
|
||||||
|
Function(int) onSelected}) =>
|
||||||
|
Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: PopupMenuButton<int>(
|
||||||
|
shape:
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
elevation: 1,
|
||||||
|
tooltip: tooltip,
|
||||||
|
child: child,
|
||||||
|
itemBuilder: (context) => itemBuilder,
|
||||||
|
onSelected: (value) => onSelected(value),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
_confirmMarkListened(BuildContext context) => generalDialog(
|
_confirmMarkListened(BuildContext context) => generalDialog(
|
||||||
context,
|
context,
|
||||||
title: Text(context.s.markConfirm),
|
title: Text(context.s.markConfirm),
|
||||||
|
@ -261,34 +308,6 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
double _topHeight = 0;
|
|
||||||
|
|
||||||
ScrollController _controller;
|
|
||||||
|
|
||||||
/// Episodes num load first time.
|
|
||||||
int _top;
|
|
||||||
|
|
||||||
/// Load more episodes when scroll to bottom.
|
|
||||||
bool _loadMore;
|
|
||||||
|
|
||||||
/// Change sort by.
|
|
||||||
bool _reverse;
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_loadMore = false;
|
|
||||||
_top = 99;
|
|
||||||
_reverse = false;
|
|
||||||
_controller = ScrollController();
|
|
||||||
_scroll = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color _color = widget.podcastLocal.primaryColor.colorizedark();
|
Color _color = widget.podcastLocal.primaryColor.colorizedark();
|
||||||
|
@ -321,8 +340,11 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: FutureBuilder<List<EpisodeBrief>>(
|
child: FutureBuilder<List<EpisodeBrief>>(
|
||||||
future:
|
future: _getRssItem(widget.podcastLocal,
|
||||||
_getRssItem(widget.podcastLocal, _top, _reverse),
|
count: _top,
|
||||||
|
reverse: _reverse,
|
||||||
|
filter: _filter,
|
||||||
|
query: _query),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) print(snapshot.error);
|
if (snapshot.hasError) print(snapshot.error);
|
||||||
return (snapshot.hasData)
|
return (snapshot.hasData)
|
||||||
|
@ -339,16 +361,14 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
Duration(seconds: 3));
|
Duration(seconds: 3));
|
||||||
if (mounted)
|
if (mounted)
|
||||||
setState(() {
|
setState(() {
|
||||||
_top = _top + 33;
|
_top = _top + 36;
|
||||||
_loadMore = false;
|
_loadMore = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (_controller.offset > 0 &&
|
if (_controller.offset > 0 &&
|
||||||
mounted &&
|
mounted &&
|
||||||
!_scroll)
|
!_scroll)
|
||||||
setState(() {
|
setState(() => _scroll = true);
|
||||||
_scroll = true;
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
physics:
|
physics:
|
||||||
const AlwaysScrollableScrollPhysics(),
|
const AlwaysScrollableScrollPhysics(),
|
||||||
|
@ -356,26 +376,36 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
PopupMenuButton<int>(
|
_customPopupMenu(
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10))),
|
|
||||||
elevation: 2,
|
|
||||||
tooltip: s.menu,
|
tooltip: s.menu,
|
||||||
itemBuilder: (context) => [
|
onSelected: (int value) {
|
||||||
widget.podcastLocal.link != null
|
switch (value) {
|
||||||
? PopupMenuItem(
|
case 0:
|
||||||
|
widget.podcastLocal.link
|
||||||
|
.launchUrl;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
widget.podcastLocal.rssUrl
|
||||||
|
.launchUrl;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
_confirmMarkListened(context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: [
|
||||||
|
if (widget.podcastLocal.link !=
|
||||||
|
null)
|
||||||
|
PopupMenuItem(
|
||||||
value: 0,
|
value: 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.only(
|
padding:
|
||||||
left: 10),
|
EdgeInsets.only(left: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(Icons.link,
|
Icon(Icons.link,
|
||||||
color: Theme.of(
|
color: context
|
||||||
context)
|
.textColor),
|
||||||
.tabBarTheme
|
|
||||||
.labelColor),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets
|
padding: EdgeInsets
|
||||||
.symmetric(
|
.symmetric(
|
||||||
|
@ -386,8 +416,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
: Center(),
|
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: 1,
|
value: 1,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -397,9 +426,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(
|
Icon(
|
||||||
Icons.rss_feed,
|
Icons.rss_feed,
|
||||||
color: Theme.of(context)
|
color: context.textColor,
|
||||||
.tabBarTheme
|
|
||||||
.labelColor,
|
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding:
|
||||||
|
@ -427,9 +454,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
painter:
|
painter:
|
||||||
ListenedAllPainter(
|
ListenedAllPainter(
|
||||||
context
|
context
|
||||||
.textTheme
|
.textColor,
|
||||||
.bodyText1
|
|
||||||
.color,
|
|
||||||
stroke: 2)),
|
stroke: 2)),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -445,35 +470,18 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onSelected: (int value) {
|
),
|
||||||
switch (value) {
|
|
||||||
case 0:
|
|
||||||
_launchUrl(
|
|
||||||
widget.podcastLocal.link);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
_launchUrl(
|
|
||||||
widget.podcastLocal.rssUrl);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
_confirmMarkListened(context);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
iconTheme: IconThemeData(
|
iconTheme: IconThemeData(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
expandedHeight: 150 +
|
expandedHeight: 150 + context.paddingTop,
|
||||||
MediaQuery.of(context).padding.top,
|
|
||||||
backgroundColor: _color,
|
backgroundColor: _color,
|
||||||
floating: true,
|
floating: true,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
flexibleSpace: LayoutBuilder(builder:
|
flexibleSpace: LayoutBuilder(
|
||||||
(BuildContext context,
|
builder: (context, constraints) {
|
||||||
BoxConstraints constraints) {
|
|
||||||
_topHeight = constraints.biggest.height;
|
_topHeight = constraints.biggest.height;
|
||||||
return FlexibleSpaceBar(
|
return FlexibleSpaceBar(
|
||||||
background: Stack(
|
background: Stack(
|
||||||
|
@ -481,9 +489,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
top: 120 +
|
top: 120 +
|
||||||
MediaQuery.of(context)
|
context.paddingTop),
|
||||||
.padding
|
|
||||||
.top),
|
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 80, right: 120),
|
left: 80, right: 120),
|
||||||
color: Colors.white10,
|
color: Colors.white10,
|
||||||
|
@ -506,18 +512,17 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color:
|
||||||
Colors.white)),
|
Colors.white)),
|
||||||
widget.podcastLocal.provider
|
if (widget.podcastLocal
|
||||||
.isNotEmpty
|
.provider.isNotEmpty)
|
||||||
? Text(
|
Text(
|
||||||
s.hostedOn(widget
|
s.hostedOn(widget
|
||||||
.podcastLocal
|
.podcastLocal
|
||||||
.provider),
|
.provider),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors
|
color:
|
||||||
.white),
|
Colors.white),
|
||||||
)
|
),
|
||||||
: Center(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -539,10 +544,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
title: _topHeight <
|
title: _topHeight <
|
||||||
70 +
|
70 + context.paddingTop
|
||||||
MediaQuery.of(context)
|
|
||||||
.padding
|
|
||||||
.top
|
|
||||||
? Text(widget.podcastLocal.title,
|
? Text(widget.podcastLocal.title,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow:
|
overflow:
|
||||||
|
@ -563,12 +565,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Material(
|
Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: PopupMenuButton<int>(
|
child: _customPopupMenu(
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(
|
|
||||||
10)),
|
|
||||||
elevation: 1,
|
|
||||||
tooltip: s.homeSubMenuSortBy,
|
tooltip: s.homeSubMenuSortBy,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 30,
|
height: 30,
|
||||||
|
@ -581,12 +578,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(s
|
Text(s
|
||||||
.homeSubMenuSortBy),
|
.homeSubMenuSortBy),
|
||||||
Padding(
|
SizedBox(width: 10),
|
||||||
padding: EdgeInsets
|
|
||||||
.symmetric(
|
|
||||||
horizontal:
|
|
||||||
5),
|
|
||||||
),
|
|
||||||
Icon(
|
Icon(
|
||||||
_reverse
|
_reverse
|
||||||
? LineIcons
|
? LineIcons
|
||||||
|
@ -597,7 +589,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
itemBuilder: (context) => [
|
itemBuilder: [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: 0,
|
value: 0,
|
||||||
child:
|
child:
|
||||||
|
@ -619,6 +611,124 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: _customPopupMenu(
|
||||||
|
tooltip: 'Filter',
|
||||||
|
child: Container(
|
||||||
|
height: 30,
|
||||||
|
padding:
|
||||||
|
EdgeInsets.symmetric(
|
||||||
|
horizontal: 15),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize:
|
||||||
|
MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text('Filter'),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Icon(
|
||||||
|
LineIcons
|
||||||
|
.filter_solid,
|
||||||
|
size: 18,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
itemBuilder: [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 0,
|
||||||
|
child: Text('All'),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 1,
|
||||||
|
child: Text(
|
||||||
|
'Not listened'),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 2,
|
||||||
|
child: Text('Liked'),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 3,
|
||||||
|
child:
|
||||||
|
Text('Downloaded'),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 4,
|
||||||
|
child: Text('Search'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onSelected: (value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
if (_filter !=
|
||||||
|
Filter.all)
|
||||||
|
setState(() {
|
||||||
|
_hideListened =
|
||||||
|
false;
|
||||||
|
_filter =
|
||||||
|
Filter.all;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
setState(() =>
|
||||||
|
_hideListened =
|
||||||
|
true);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (_filter !=
|
||||||
|
Filter.liked)
|
||||||
|
setState(() =>
|
||||||
|
_filter = Filter
|
||||||
|
.liked);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (_filter !=
|
||||||
|
Filter.downloaded)
|
||||||
|
setState(() =>
|
||||||
|
_filter = Filter
|
||||||
|
.downloaded);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
showGeneralDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible:
|
||||||
|
true,
|
||||||
|
barrierLabel:
|
||||||
|
MaterialLocalizations
|
||||||
|
.of(
|
||||||
|
context)
|
||||||
|
.modalBarrierDismissLabel,
|
||||||
|
barrierColor:
|
||||||
|
Colors
|
||||||
|
.black54,
|
||||||
|
transitionDuration:
|
||||||
|
const Duration(
|
||||||
|
milliseconds:
|
||||||
|
200),
|
||||||
|
pageBuilder: (BuildContext
|
||||||
|
context,
|
||||||
|
Animation
|
||||||
|
animaiton,
|
||||||
|
Animation
|
||||||
|
secondaryAnimation) =>
|
||||||
|
SearchEpisdoe(
|
||||||
|
onSearch:
|
||||||
|
(query) {
|
||||||
|
setState(
|
||||||
|
() {
|
||||||
|
_query =
|
||||||
|
query;
|
||||||
|
_filter =
|
||||||
|
Filter.search;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Material(
|
Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
|
@ -692,6 +802,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
reverse: _reverse,
|
reverse: _reverse,
|
||||||
episodeCount: _episodeCount,
|
episodeCount: _episodeCount,
|
||||||
initNum: _scroll ? 0 : 12,
|
initNum: _scroll ? 0 : 12,
|
||||||
|
hideListened: _hideListened,
|
||||||
),
|
),
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
@ -834,3 +945,111 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SearchEpisdoe extends StatefulWidget {
|
||||||
|
SearchEpisdoe({this.onSearch, Key key}) : super(key: key);
|
||||||
|
final ValueChanged<String> onSearch;
|
||||||
|
@override
|
||||||
|
_SearchEpisodeState createState() => _SearchEpisodeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchEpisodeState extends State<SearchEpisdoe> {
|
||||||
|
TextEditingController _controller;
|
||||||
|
String _query;
|
||||||
|
int _error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_error = 0;
|
||||||
|
_controller = TextEditingController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final s = context.s;
|
||||||
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
|
value: SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
systemNavigationBarColor:
|
||||||
|
Theme.of(context).brightness == Brightness.light
|
||||||
|
? Color.fromRGBO(113, 113, 113, 1)
|
||||||
|
: Color.fromRGBO(5, 5, 5, 1),
|
||||||
|
),
|
||||||
|
child: AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
|
elevation: 1,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
titlePadding:
|
||||||
|
const EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
|
||||||
|
actionsPadding: EdgeInsets.all(0),
|
||||||
|
actions: <Widget>[
|
||||||
|
FlatButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(
|
||||||
|
s.cancel,
|
||||||
|
style: TextStyle(color: Colors.grey[600]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FlatButton(
|
||||||
|
onPressed: (_query != null && _query != '')
|
||||||
|
? () {
|
||||||
|
{
|
||||||
|
widget.onSearch(_query);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(s.confirm,
|
||||||
|
style: TextStyle(color: Theme.of(context).accentColor)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
title: SizedBox(width: context.width - 160, child: Text(s.newGroup)),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
|
hintText: s.newGroup,
|
||||||
|
hintStyle: TextStyle(fontSize: 18),
|
||||||
|
filled: true,
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide:
|
||||||
|
BorderSide(color: context.accentColor, width: 2.0),
|
||||||
|
),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide:
|
||||||
|
BorderSide(color: context.accentColor, width: 2.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
cursorRadius: Radius.circular(2),
|
||||||
|
autofocus: true,
|
||||||
|
maxLines: 1,
|
||||||
|
controller: _controller,
|
||||||
|
onChanged: (value) {
|
||||||
|
_query = value;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: (_error == 1)
|
||||||
|
? Text(
|
||||||
|
s.groupExisted,
|
||||||
|
style: TextStyle(color: Colors.red[400]),
|
||||||
|
)
|
||||||
|
: Center(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:audio_service/audio_service.dart';
|
import 'package:audio_service/audio_service.dart';
|
||||||
|
|
||||||
class EpisodeBrief {
|
class EpisodeBrief {
|
||||||
|
@ -27,35 +26,18 @@ class EpisodeBrief {
|
||||||
this.pubDate,
|
this.pubDate,
|
||||||
this.feedTitle,
|
this.feedTitle,
|
||||||
this.primaryColor,
|
this.primaryColor,
|
||||||
this.liked,
|
|
||||||
this.downloaded,
|
|
||||||
this.duration,
|
this.duration,
|
||||||
this.explicit,
|
this.explicit,
|
||||||
this.imagePath,
|
this.imagePath,
|
||||||
this.mediaId,
|
{this.mediaId,
|
||||||
|
this.liked,
|
||||||
|
this.downloaded,
|
||||||
this.isNew,
|
this.isNew,
|
||||||
this.skipSeconds,
|
this.skipSeconds,
|
||||||
{this.description = '',
|
this.description = '',
|
||||||
this.downloadDate = 0})
|
this.downloadDate = 0})
|
||||||
: assert(enclosureUrl != null);
|
: assert(enclosureUrl != null);
|
||||||
|
|
||||||
String ateToString() {
|
|
||||||
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
|
|
||||||
var diffrence = DateTime.now().toUtc().difference(date);
|
|
||||||
if (diffrence.inHours < 1) {
|
|
||||||
return '1 hour ago';
|
|
||||||
} else if (diffrence.inHours < 24) {
|
|
||||||
return '${diffrence.inHours} hours ago';
|
|
||||||
} else if (diffrence.inHours == 24) {
|
|
||||||
return '1 day ago';
|
|
||||||
} else if (diffrence.inDays < 7) {
|
|
||||||
return '${diffrence.inDays} days ago';
|
|
||||||
} else {
|
|
||||||
return DateFormat.yMMMd().format(
|
|
||||||
DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true).toLocal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaItem toMediaItem() {
|
MediaItem toMediaItem() {
|
||||||
return MediaItem(
|
return MediaItem(
|
||||||
id: mediaId,
|
id: mediaId,
|
||||||
|
|
|
@ -34,9 +34,14 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
final int episodeCount;
|
final int episodeCount;
|
||||||
final Layout layout;
|
final Layout layout;
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
|
|
||||||
final int initNum;
|
final int initNum;
|
||||||
EpisodeGrid({
|
|
||||||
Key key,
|
/// Hide listened episode.
|
||||||
|
final bool hideListened;
|
||||||
|
|
||||||
|
EpisodeGrid(
|
||||||
|
{Key key,
|
||||||
@required this.episodes,
|
@required this.episodes,
|
||||||
this.initNum = 12,
|
this.initNum = 12,
|
||||||
this.showDownload = false,
|
this.showDownload = false,
|
||||||
|
@ -45,20 +50,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
this.episodeCount = 0,
|
this.episodeCount = 0,
|
||||||
this.layout = Layout.three,
|
this.layout = Layout.three,
|
||||||
this.reverse,
|
this.reverse,
|
||||||
}) : super(key: key);
|
this.hideListened = false})
|
||||||
String _dateToString(BuildContext context, {int pubDate}) {
|
: super(key: key);
|
||||||
final s = context.s;
|
|
||||||
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
|
|
||||||
var difference = DateTime.now().toUtc().difference(date);
|
|
||||||
if (difference.inHours < 24) {
|
|
||||||
return s.hoursAgo(difference.inHours);
|
|
||||||
} else if (difference.inDays < 7) {
|
|
||||||
return s.daysAgo(difference.inDays);
|
|
||||||
} else {
|
|
||||||
return DateFormat.yMMMd().format(
|
|
||||||
DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true).toLocal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<int> _isListened(EpisodeBrief episode) async {
|
Future<int> _isListened(EpisodeBrief episode) async {
|
||||||
DBHelper dbHelper = DBHelper();
|
DBHelper dbHelper = DBHelper();
|
||||||
|
@ -123,6 +116,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Episode title widget.
|
||||||
Widget _title(EpisodeBrief episode) => Container(
|
Widget _title(EpisodeBrief episode) => Container(
|
||||||
alignment:
|
alignment:
|
||||||
layout == Layout.one ? Alignment.centerLeft : Alignment.topLeft,
|
layout == Layout.one ? Alignment.centerLeft : Alignment.topLeft,
|
||||||
|
@ -135,6 +129,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Circel avatar widget.
|
||||||
Widget _circleImage(BuildContext context,
|
Widget _circleImage(BuildContext context,
|
||||||
{EpisodeBrief episode, Color color, bool boo}) =>
|
{EpisodeBrief episode, Color color, bool boo}) =>
|
||||||
Container(
|
Container(
|
||||||
|
@ -170,6 +165,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
: Center()
|
: Center()
|
||||||
: Center();
|
: Center();
|
||||||
|
|
||||||
|
/// New indicator widget.
|
||||||
Widget _isNewIndicator(EpisodeBrief episode) => episode.isNew == 1
|
Widget _isNewIndicator(EpisodeBrief episode) => episode.isNew == 1
|
||||||
? Container(
|
? Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 2),
|
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
@ -178,6 +174,7 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: Center();
|
: Center();
|
||||||
|
|
||||||
|
/// Count indicator widget.
|
||||||
Widget _numberIndicater(BuildContext context, {int index, Color color}) =>
|
Widget _numberIndicater(BuildContext context, {int index, Color color}) =>
|
||||||
showNumber
|
showNumber
|
||||||
? Container(
|
? Container(
|
||||||
|
@ -196,9 +193,10 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: Center();
|
: Center();
|
||||||
|
|
||||||
|
/// Pubdate widget
|
||||||
Widget _pubDate(BuildContext context, {EpisodeBrief episode, Color color}) =>
|
Widget _pubDate(BuildContext context, {EpisodeBrief episode, Color color}) =>
|
||||||
Text(
|
Text(
|
||||||
_dateToString(context, pubDate: episode.pubDate),
|
episode.pubDate.toDate(context),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: context.width / 35,
|
fontSize: context.width / 35,
|
||||||
color: color,
|
color: color,
|
||||||
|
@ -259,7 +257,9 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
bool isDownloaded = snapshot.data.item3;
|
bool isDownloaded = snapshot.data.item3;
|
||||||
bool tapToOpen = snapshot.data.item4;
|
bool tapToOpen = snapshot.data.item4;
|
||||||
List<int> menuList = snapshot.data.item5;
|
List<int> menuList = snapshot.data.item5;
|
||||||
return Container(
|
return (hideListened && isListened > 0)
|
||||||
|
? Center()
|
||||||
|
: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.all(Radius.circular(5.0)),
|
BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
@ -270,7 +270,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
: context.scaffoldBackgroundColor,
|
: context.scaffoldBackgroundColor,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: context.brightness == Brightness.light
|
color:
|
||||||
|
context.brightness == Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: Color.fromRGBO(40, 40, 40, 1),
|
: Color.fromRGBO(40, 40, 40, 1),
|
||||||
blurRadius: 0.5,
|
blurRadius: 0.5,
|
||||||
|
@ -283,7 +284,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.all(Radius.circular(5.0)),
|
BorderRadius.all(Radius.circular(5.0)),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: context.brightness == Brightness.light
|
color:
|
||||||
|
context.brightness == Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.scaffoldBackgroundColor,
|
: context.scaffoldBackgroundColor,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
|
@ -294,11 +296,12 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
menuItemExtent: 45,
|
menuItemExtent: 45,
|
||||||
menuBoxDecoration: BoxDecoration(
|
menuBoxDecoration: BoxDecoration(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
borderRadius:
|
borderRadius: BorderRadius.all(
|
||||||
BorderRadius.all(Radius.circular(15.0))),
|
Radius.circular(15.0))),
|
||||||
duration: Duration(milliseconds: 100),
|
duration: Duration(milliseconds: 100),
|
||||||
tapMode:
|
tapMode: tapToOpen
|
||||||
tapToOpen ? TapMode.onTap : TapMode.onLongPress,
|
? TapMode.onTap
|
||||||
|
: TapMode.onLongPress,
|
||||||
animateMenuItems: false,
|
animateMenuItems: false,
|
||||||
blurBackgroundColor:
|
blurBackgroundColor:
|
||||||
context.brightness == Brightness.light
|
context.brightness == Brightness.light
|
||||||
|
@ -308,11 +311,12 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
menuOffset: 6,
|
menuOffset: 6,
|
||||||
menuItems: <FocusedMenuItem>[
|
menuItems: <FocusedMenuItem>[
|
||||||
FocusedMenuItem(
|
FocusedMenuItem(
|
||||||
backgroundColor:
|
backgroundColor: context.brightness ==
|
||||||
context.brightness == Brightness.light
|
Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.scaffoldBackgroundColor,
|
: context.scaffoldBackgroundColor,
|
||||||
title: Text(data.item1 != episodes[index]
|
title: Text(
|
||||||
|
data.item1 != episodes[index]
|
||||||
? s.play
|
? s.play
|
||||||
: s.playing),
|
: s.playing),
|
||||||
trailingIcon: Icon(
|
trailingIcon: Icon(
|
||||||
|
@ -325,12 +329,15 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
}),
|
}),
|
||||||
menuList.contains(1)
|
menuList.contains(1)
|
||||||
? FocusedMenuItem(
|
? FocusedMenuItem(
|
||||||
backgroundColor:
|
backgroundColor: context
|
||||||
context.brightness == Brightness.light
|
.brightness ==
|
||||||
|
Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.scaffoldBackgroundColor,
|
: context
|
||||||
|
.scaffoldBackgroundColor,
|
||||||
title: data.item2.contains(
|
title: data.item2.contains(
|
||||||
episodes[index].enclosureUrl)
|
episodes[index]
|
||||||
|
.enclosureUrl)
|
||||||
? Text(s.remove)
|
? Text(s.remove)
|
||||||
: Text(s.later),
|
: Text(s.later),
|
||||||
trailingIcon: Icon(
|
trailingIcon: Icon(
|
||||||
|
@ -339,15 +346,17 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (!data.item2.contains(
|
if (!data.item2.contains(
|
||||||
episodes[index].enclosureUrl)) {
|
episodes[index]
|
||||||
audio.addToPlaylist(episodes[index]);
|
.enclosureUrl)) {
|
||||||
|
audio.addToPlaylist(
|
||||||
|
episodes[index]);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: s.toastAddPlaylist,
|
msg: s.toastAddPlaylist,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
audio
|
audio.delFromPlaylist(
|
||||||
.delFromPlaylist(episodes[index]);
|
episodes[index]);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: s.toastRemovePlaylist,
|
msg: s.toastRemovePlaylist,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
@ -357,10 +366,12 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
: null,
|
: null,
|
||||||
menuList.contains(2)
|
menuList.contains(2)
|
||||||
? FocusedMenuItem(
|
? FocusedMenuItem(
|
||||||
backgroundColor:
|
backgroundColor: context
|
||||||
context.brightness == Brightness.light
|
.brightness ==
|
||||||
|
Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.scaffoldBackgroundColor,
|
: context
|
||||||
|
.scaffoldBackgroundColor,
|
||||||
title: isLiked
|
title: isLiked
|
||||||
? Text(s.unlike)
|
? Text(s.unlike)
|
||||||
: Text(s.like),
|
: Text(s.like),
|
||||||
|
@ -369,15 +380,16 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (isLiked) {
|
if (isLiked) {
|
||||||
await _setUnliked(
|
await _setUnliked(
|
||||||
episodes[index].enclosureUrl);
|
episodes[index]
|
||||||
|
.enclosureUrl);
|
||||||
audio.setEpisodeState = true;
|
audio.setEpisodeState = true;
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: s.unliked,
|
msg: s.unliked,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await _saveLiked(
|
await _saveLiked(episodes[index]
|
||||||
episodes[index].enclosureUrl);
|
.enclosureUrl);
|
||||||
audio.setEpisodeState = true;
|
audio.setEpisodeState = true;
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: s.liked,
|
msg: s.liked,
|
||||||
|
@ -388,10 +400,12 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
: null,
|
: null,
|
||||||
menuList.contains(3)
|
menuList.contains(3)
|
||||||
? FocusedMenuItem(
|
? FocusedMenuItem(
|
||||||
backgroundColor:
|
backgroundColor: context
|
||||||
context.brightness == Brightness.light
|
.brightness ==
|
||||||
|
Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.scaffoldBackgroundColor,
|
: context
|
||||||
|
.scaffoldBackgroundColor,
|
||||||
title: isListened > 0
|
title: isListened > 0
|
||||||
? Text(s.listened,
|
? Text(s.listened,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -400,7 +414,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
: Text(
|
: Text(
|
||||||
s.markListened,
|
s.markListened,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow:
|
||||||
|
TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
trailingIcon: SizedBox(
|
trailingIcon: SizedBox(
|
||||||
width: 23,
|
width: 23,
|
||||||
|
@ -412,7 +427,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (isListened < 1) {
|
if (isListened < 1) {
|
||||||
await _markListened(episodes[index]);
|
await _markListened(
|
||||||
|
episodes[index]);
|
||||||
audio.setEpisodeState = true;
|
audio.setEpisodeState = true;
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: s.markListened,
|
msg: s.markListened,
|
||||||
|
@ -423,10 +439,12 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
: null,
|
: null,
|
||||||
menuList.contains(4)
|
menuList.contains(4)
|
||||||
? FocusedMenuItem(
|
? FocusedMenuItem(
|
||||||
backgroundColor:
|
backgroundColor: context
|
||||||
context.brightness == Brightness.light
|
.brightness ==
|
||||||
|
Brightness.light
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.scaffoldBackgroundColor,
|
: context
|
||||||
|
.scaffoldBackgroundColor,
|
||||||
title: isDownloaded
|
title: isDownloaded
|
||||||
? Text(s.downloaded,
|
? Text(s.downloaded,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -438,7 +456,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
color: Colors.green),
|
color: Colors.green),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (!isDownloaded)
|
if (!isDownloaded)
|
||||||
downloader.startTask(episodes[index]);
|
downloader
|
||||||
|
.startTask(episodes[index]);
|
||||||
})
|
})
|
||||||
: null
|
: null
|
||||||
],
|
],
|
||||||
|
@ -446,7 +465,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: layout == Layout.one ? 1 : 2,
|
flex: layout == Layout.one ? 1 : 2,
|
||||||
|
@ -484,15 +504,16 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
CrossAxisAlignment.center,
|
CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
_circleImage(context,
|
_circleImage(context,
|
||||||
episode: episodes[index],
|
episode:
|
||||||
|
episodes[index],
|
||||||
color: _c,
|
color: _c,
|
||||||
boo: boo),
|
boo: boo),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 5,
|
width: 5,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child:
|
child: _title(
|
||||||
_title(episodes[index]))
|
episodes[index]))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -504,23 +525,28 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (layout != Layout.one)
|
if (layout != Layout.one)
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomLeft,
|
alignment:
|
||||||
|
Alignment.bottomLeft,
|
||||||
child: _pubDate(context,
|
child: _pubDate(context,
|
||||||
episode: episodes[index],
|
episode: episodes[index],
|
||||||
color: _c),
|
color: _c),
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
layout != Layout.three &&
|
layout != Layout.three &&
|
||||||
episodes[index].duration != 0
|
episodes[index]
|
||||||
|
.duration !=
|
||||||
|
0
|
||||||
? Container(
|
? Container(
|
||||||
alignment: Alignment.center,
|
alignment:
|
||||||
|
Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
_stringForSeconds(
|
_stringForSeconds(
|
||||||
episodes[index]
|
episodes[index]
|
||||||
.duration
|
.duration
|
||||||
.toDouble()),
|
.toDouble()),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: _width / 35),
|
fontSize:
|
||||||
|
_width / 35),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Center(),
|
: Center(),
|
||||||
|
@ -549,7 +575,8 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
.enclosureLength !=
|
.enclosureLength !=
|
||||||
0
|
0
|
||||||
? Container(
|
? Container(
|
||||||
alignment: Alignment.center,
|
alignment:
|
||||||
|
Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
((episodes[index]
|
((episodes[index]
|
||||||
.enclosureLength) ~/
|
.enclosureLength) ~/
|
||||||
|
@ -557,18 +584,21 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
.toString() +
|
.toString() +
|
||||||
'MB',
|
'MB',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: _width / 35),
|
fontSize:
|
||||||
|
_width / 35),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Center(),
|
: Center(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(1),
|
padding: EdgeInsets.all(1),
|
||||||
),
|
),
|
||||||
showFavorite || layout != Layout.three
|
showFavorite ||
|
||||||
|
layout != Layout.three
|
||||||
? isLiked
|
? isLiked
|
||||||
? IconTheme(
|
? IconTheme(
|
||||||
data: IconThemeData(
|
data: IconThemeData(
|
||||||
size: _width / 35),
|
size:
|
||||||
|
_width / 35),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.favorite,
|
Icons.favorite,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import '../generated/l10n.dart';
|
import '../generated/l10n.dart';
|
||||||
|
@ -14,6 +15,7 @@ extension ContextExtension on BuildContext {
|
||||||
Brightness get brightness => Theme.of(this).brightness;
|
Brightness get brightness => Theme.of(this).brightness;
|
||||||
double get width => MediaQuery.of(this).size.width;
|
double get width => MediaQuery.of(this).size.width;
|
||||||
double get height => MediaQuery.of(this).size.height;
|
double get height => MediaQuery.of(this).size.height;
|
||||||
|
double get paddingTop => MediaQuery.of(this).padding.top;
|
||||||
TextTheme get textTheme => Theme.of(this).textTheme;
|
TextTheme get textTheme => Theme.of(this).textTheme;
|
||||||
S get s => S.of(this);
|
S get s => S.of(this);
|
||||||
}
|
}
|
||||||
|
@ -58,6 +60,10 @@ extension StringExtension on String {
|
||||||
await launch(this);
|
await launch(this);
|
||||||
} else {
|
} else {
|
||||||
print('Could not launch $this');
|
print('Could not launch $this');
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: '$this Invalid Link',
|
||||||
|
gravity: ToastGravity.TOP,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue