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,
"qualifiers.${WikidataProperties.placeOfPublication}.*.datavalue.value.id")
.firstOrNull ??
"no country");
"unknown location");
return DateWithPrecisionAndCountry(DateTime.parse(value["time"]),
_precisionFromWikidata(value["precision"]), country);
}).toList();
@ -153,6 +153,7 @@ class WikidataMovieData extends MovieData {
WikidataMovieData(title, releaseDates[0], entityId);
movie.setDetails(
titles: titles,
releaseDates: releaseDates,
genres: genres,
);
return movie;
@ -168,7 +169,8 @@ SELECT
WHERE {
?movie wdt:P31 wd:Q11424; # Q11424 is the item for "film"
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
ORDER BY ?minReleaseDate

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:release_schedule/api/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/view/movie_manager_list.dart';
@ -37,11 +38,7 @@ class HomePage extends StatelessWidget {
return Scaffold(
appBar: AppBar(
title: const Text("Release Schedule"),
actions: [
FilledButton(
onPressed: () => manager.removeMoviesWhere((movie) => true),
child: const Icon(Icons.delete))
],
actions: [HamburgerMenu(manager)],
),
body: DefaultTabController(
length: 2,
@ -51,7 +48,12 @@ class HomePage extends StatelessWidget {
child: TabBarView(
children: [
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(
child: const Icon(Icons.refresh),
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 }
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});
class DateWithPrecisionAndCountry {
@ -167,7 +185,11 @@ class DateWithPrecisionAndCountry {
@override
String toString() {
String dateString = switch (precision) {
return "${toDateString()} ($country)";
}
String toDateString() {
return switch (precision) {
DatePrecision.decade =>
"${DateFormat("yyyy").format(date).substring(0, 3)}0s",
DatePrecision.year => date.year.toString(),
@ -176,7 +198,6 @@ class DateWithPrecisionAndCountry {
DatePrecision.hour => DateFormat("MMMM d, yyyy, HH").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:release_schedule/model/date_format.dart';
import 'package:release_schedule/model/movie.dart';
import 'package:release_schedule/view/movie_page.dart';
class MovieItem extends StatelessWidget {
final MovieData movie;
@ -21,6 +22,16 @@ class MovieItem extends StatelessWidget {
: Icons.bookmark_border),
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() ??
[],
),
],
),
),
),
);
},
);
}
}