feature: add page for displaying a movie

fix: only load and show upcoming movies with at least month date precision
main
daniel-michel 2024-01-08 14:30:32 +01:00
parent 618f5d135b
commit 0ea9aef7be
5 changed files with 179 additions and 10 deletions

View File

@ -138,7 +138,7 @@ class WikidataMovieData extends MovieData {
String country = _getCachedLabelForEntity(selectInJson<String>(dateClaim, String country = _getCachedLabelForEntity(selectInJson<String>(dateClaim,
"qualifiers.${WikidataProperties.placeOfPublication}.*.datavalue.value.id") "qualifiers.${WikidataProperties.placeOfPublication}.*.datavalue.value.id")
.firstOrNull ?? .firstOrNull ??
"no country"); "unknown location");
return DateWithPrecisionAndCountry(DateTime.parse(value["time"]), return DateWithPrecisionAndCountry(DateTime.parse(value["time"]),
_precisionFromWikidata(value["precision"]), country); _precisionFromWikidata(value["precision"]), country);
}).toList(); }).toList();
@ -153,6 +153,7 @@ class WikidataMovieData extends MovieData {
WikidataMovieData(title, releaseDates[0], entityId); WikidataMovieData(title, releaseDates[0], entityId);
movie.setDetails( movie.setDetails(
titles: titles, titles: titles,
releaseDates: releaseDates,
genres: genres, genres: genres,
); );
return movie; return movie;
@ -168,7 +169,8 @@ SELECT
WHERE { WHERE {
?movie wdt:P31 wd:Q11424; # Q11424 is the item for "film" ?movie wdt:P31 wd:Q11424; # Q11424 is the item for "film"
wdt:P577 ?releaseDate. # P577 is the "publication date" property wdt:P577 ?releaseDate. # P577 is the "publication date" property
FILTER (xsd:date(?releaseDate) >= xsd:date("$date"^^xsd:dateTime)) ?movie p:P577/psv:P577 [wikibase:timePrecision ?precision].
FILTER (xsd:date(?releaseDate) >= xsd:date("$date"^^xsd:dateTime) && ?precision >= 10)
} }
GROUP BY ?movie GROUP BY ?movie
ORDER BY ?minReleaseDate ORDER BY ?minReleaseDate

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:release_schedule/api/movie_api.dart'; import 'package:release_schedule/api/movie_api.dart';
import 'package:release_schedule/api/wikidata_movie_api.dart'; import 'package:release_schedule/api/wikidata_movie_api.dart';
import 'package:release_schedule/model/movie.dart';
import 'package:release_schedule/model/movie_manager.dart'; import 'package:release_schedule/model/movie_manager.dart';
import 'package:release_schedule/view/movie_manager_list.dart'; import 'package:release_schedule/view/movie_manager_list.dart';
@ -37,11 +38,7 @@ class HomePage extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text("Release Schedule"), title: const Text("Release Schedule"),
actions: [ actions: [HamburgerMenu(manager)],
FilledButton(
onPressed: () => manager.removeMoviesWhere((movie) => true),
child: const Icon(Icons.delete))
],
), ),
body: DefaultTabController( body: DefaultTabController(
length: 2, length: 2,
@ -51,7 +48,12 @@ class HomePage extends StatelessWidget {
child: TabBarView( child: TabBarView(
children: [ children: [
Scaffold( Scaffold(
body: MovieManagerList(manager), body: MovieManagerList(
manager,
// Only show movies that have a release date with at least month precision
filter: (movie) =>
movie.releaseDate.precision >= DatePrecision.month,
),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh), child: const Icon(Icons.refresh),
onPressed: () => manager.loadUpcomingMovies(), onPressed: () => manager.loadUpcomingMovies(),
@ -71,3 +73,28 @@ class HomePage extends StatelessWidget {
); );
} }
} }
class HamburgerMenu extends StatelessWidget {
final MovieManager manager;
const HamburgerMenu(this.manager, {super.key});
@override
Widget build(BuildContext context) {
return PopupMenuButton(
icon: const Icon(Icons.menu),
itemBuilder: (context) {
return [
PopupMenuItem(
child: const Text("Remove all not bookmarked"),
onTap: () =>
manager.removeMoviesWhere((movie) => !movie.bookmarked),
),
PopupMenuItem(
child: const Text("Remove all"),
onTap: () => manager.removeMoviesWhere((movie) => true),
),
];
},
);
}
}

View File

@ -146,6 +146,24 @@ class MovieData extends ChangeNotifier {
enum DatePrecision { decade, year, month, day, hour, minute } enum DatePrecision { decade, year, month, day, hour, minute }
extension DatePrecisionComparison on DatePrecision {
bool operator <(DatePrecision other) {
return index < other.index;
}
bool operator <=(DatePrecision other) {
return index <= other.index;
}
bool operator >(DatePrecision other) {
return index > other.index;
}
bool operator >=(DatePrecision other) {
return index >= other.index;
}
}
typedef TitleInLanguage = ({String title, String language}); typedef TitleInLanguage = ({String title, String language});
class DateWithPrecisionAndCountry { class DateWithPrecisionAndCountry {
@ -167,7 +185,11 @@ class DateWithPrecisionAndCountry {
@override @override
String toString() { String toString() {
String dateString = switch (precision) { return "${toDateString()} ($country)";
}
String toDateString() {
return switch (precision) {
DatePrecision.decade => DatePrecision.decade =>
"${DateFormat("yyyy").format(date).substring(0, 3)}0s", "${DateFormat("yyyy").format(date).substring(0, 3)}0s",
DatePrecision.year => date.year.toString(), DatePrecision.year => date.year.toString(),
@ -176,7 +198,6 @@ class DateWithPrecisionAndCountry {
DatePrecision.hour => DateFormat("MMMM d, yyyy, HH").format(date), DatePrecision.hour => DateFormat("MMMM d, yyyy, HH").format(date),
DatePrecision.minute => DateFormat("MMMM d, yyyy, HH:mm").format(date) DatePrecision.minute => DateFormat("MMMM d, yyyy, HH:mm").format(date)
}; };
return "$dateString ($country)";
} }
} }

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:release_schedule/model/date_format.dart'; import 'package:release_schedule/model/date_format.dart';
import 'package:release_schedule/model/movie.dart'; import 'package:release_schedule/model/movie.dart';
import 'package:release_schedule/view/movie_page.dart';
class MovieItem extends StatelessWidget { class MovieItem extends StatelessWidget {
final MovieData movie; final MovieData movie;
@ -21,6 +22,16 @@ class MovieItem extends StatelessWidget {
: Icons.bookmark_border), : Icons.bookmark_border),
onPressed: () => movie.setDetails(bookmarked: !movie.bookmarked), onPressed: () => movie.setDetails(bookmarked: !movie.bookmarked),
), ),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return MoviePage(movie);
},
),
);
},
); );
}, },
); );

View File

@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
import 'package:release_schedule/model/movie.dart';
class Heading extends StatelessWidget {
final String text;
const Heading(this.text, {super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20, bottom: 10),
child: Text(
text,
style: Theme.of(context).textTheme.headlineSmall,
),
);
}
}
class MoviePage extends StatelessWidget {
final MovieData movie;
const MoviePage(this.movie, {super.key});
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: movie,
builder: (context, child) {
return Scaffold(
appBar: AppBar(
title: Text(movie.title),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
spacing: 10,
runSpacing: 10,
children: movie.genres
?.map((genre) => Chip(label: Text(genre)))
.toList() ??
[],
),
const SizedBox(height: 20),
const Heading("Titles"),
Table(
border: TableBorder.symmetric(
inside: BorderSide(
color: Theme.of(context).dividerColor,
),
),
children: movie.titles?.map((title) {
return TableRow(
children: [
TableCell(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(title.language),
)),
TableCell(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(title.title),
))
],
);
}).toList() ??
[],
),
const Heading("Release Dates"),
Table(
border: TableBorder.symmetric(
inside: BorderSide(
color: Theme.of(context).dividerColor,
),
),
children: movie.releaseDates?.map((releaseDate) {
return TableRow(
children: [
TableCell(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(releaseDate.country),
)),
TableCell(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(releaseDate.toDateString()),
))
],
);
}).toList() ??
[],
),
],
),
),
),
);
},
);
}
}