Compare commits
2 Commits
9aa0278ab0
...
92b4a302dc
Author | SHA1 | Date |
---|---|---|
daniel-michel | 92b4a302dc | |
daniel-michel | 0520120ccf |
|
@ -0,0 +1,85 @@
|
||||||
|
import 'package:release_schedule/api/json_helper.dart';
|
||||||
|
import 'package:release_schedule/api/wikidata/wikidata_movie_api.dart';
|
||||||
|
import 'package:release_schedule/model/dates.dart';
|
||||||
|
import 'package:release_schedule/model/movie.dart';
|
||||||
|
|
||||||
|
class WikidataMovieData extends MovieData {
|
||||||
|
String entityId;
|
||||||
|
WikidataMovieData(this.entityId);
|
||||||
|
|
||||||
|
WikidataMovieData.fromEncodable(Map encodable)
|
||||||
|
: entityId = encodable["entityId"],
|
||||||
|
super.fromJsonEncodable(encodable);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool same(MovieData other) {
|
||||||
|
return other is WikidataMovieData && entityId == other.entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map toJsonEncodable() {
|
||||||
|
return super.toJsonEncodable()..addAll({"entityId": entityId});
|
||||||
|
}
|
||||||
|
|
||||||
|
static WikidataMovieData fromWikidataEntity(
|
||||||
|
String entityId, Map<String, dynamic> entity) {
|
||||||
|
Map<String, dynamic> claims = entity["claims"];
|
||||||
|
List<TextInLanguage>? titles = selectInJson(
|
||||||
|
claims, "${WikidataProperties.title}.*.mainsnak.datavalue.value")
|
||||||
|
.map((value) => (
|
||||||
|
text: value["text"],
|
||||||
|
language: value["language"],
|
||||||
|
) as TextInLanguage)
|
||||||
|
.toList();
|
||||||
|
List<TextInLanguage>? labels = selectInJson(entity, "labels.*")
|
||||||
|
.map((value) => (
|
||||||
|
text: value["value"],
|
||||||
|
language: value["language"],
|
||||||
|
) as TextInLanguage)
|
||||||
|
.toList();
|
||||||
|
String? wikipediaTitle = selectInJson(entity, "sitelinks.enwiki.url")
|
||||||
|
.firstOrNull
|
||||||
|
?.split("/")
|
||||||
|
.last;
|
||||||
|
Dated<String?>? description = wikipediaTitle != null
|
||||||
|
? getCachedWikipediaExplainTextFotTitle(wikipediaTitle)
|
||||||
|
: null;
|
||||||
|
List<DateWithPrecisionAndCountry> releaseDates =
|
||||||
|
_getReleaseDates(claims).toList();
|
||||||
|
// Sort release dates with higher precision to the beginning
|
||||||
|
releaseDates.sort((a, b) => -a.dateWithPrecision.precision.index
|
||||||
|
.compareTo(b.dateWithPrecision.precision.index));
|
||||||
|
List<String>? genres = selectInJson<String>(
|
||||||
|
claims, "${WikidataProperties.genre}.*.mainsnak.datavalue.value.id")
|
||||||
|
.map(getCachedLabelForEntity)
|
||||||
|
.toList();
|
||||||
|
WikidataMovieData movie = WikidataMovieData(entityId);
|
||||||
|
movie.setDetails(
|
||||||
|
titles: Dated.now(titles),
|
||||||
|
labels: Dated.now(labels),
|
||||||
|
releaseDates: Dated.now(releaseDates),
|
||||||
|
genres: Dated.now(genres),
|
||||||
|
description: description,
|
||||||
|
);
|
||||||
|
return movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Iterable<DateWithPrecisionAndCountry> _getReleaseDates(
|
||||||
|
Map<String, dynamic> claims) {
|
||||||
|
return selectInJson(claims, "${WikidataProperties.publicationDate}.*")
|
||||||
|
.where((dateClaim) => dateClaim["rank"] != "deprecated")
|
||||||
|
.expand<DateWithPrecisionAndCountry>((dateClaim) {
|
||||||
|
var value = selectInJson(dateClaim, "mainsnak.datavalue.value").first;
|
||||||
|
Iterable<String> countries = (selectInJson<String>(dateClaim,
|
||||||
|
"qualifiers.${WikidataProperties.placeOfPublication}.*.datavalue.value.id"))
|
||||||
|
.map(getCachedLabelForEntity);
|
||||||
|
if (countries.isEmpty) {
|
||||||
|
countries = ["unknown location"];
|
||||||
|
}
|
||||||
|
return countries.map((country) => DateWithPrecisionAndCountry(
|
||||||
|
DateTime.parse(value["time"]),
|
||||||
|
precisionFromWikidata(value["precision"]),
|
||||||
|
country));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,8 @@ import 'package:intl/intl.dart';
|
||||||
import 'package:release_schedule/api/api_manager.dart';
|
import 'package:release_schedule/api/api_manager.dart';
|
||||||
import 'package:release_schedule/api/json_helper.dart';
|
import 'package:release_schedule/api/json_helper.dart';
|
||||||
import 'package:release_schedule/api/movie_api.dart';
|
import 'package:release_schedule/api/movie_api.dart';
|
||||||
|
import 'package:release_schedule/api/wikidata/wikidata_movie.dart';
|
||||||
import 'package:release_schedule/model/dates.dart';
|
import 'package:release_schedule/model/dates.dart';
|
||||||
import 'package:release_schedule/model/movie.dart';
|
|
||||||
|
|
||||||
class WikidataProperties {
|
class WikidataProperties {
|
||||||
static const String instanceOf = "P31";
|
static const String instanceOf = "P31";
|
||||||
|
@ -75,7 +75,7 @@ class WikidataMovieApi implements MovieApi {
|
||||||
final start = i * batchSize;
|
final start = i * batchSize;
|
||||||
final end = min((i + 1) * batchSize, movieIds.length);
|
final end = min((i + 1) * batchSize, movieIds.length);
|
||||||
var response = await _wikidataApi.get(
|
var response = await _wikidataApi.get(
|
||||||
"&action=wbgetentities&format=json&props=labels|claims&ids=${movieIds.sublist(start, end).join("|")}");
|
"&action=wbgetentities&format=json&props=labels|claims|sitelinks/urls&ids=${movieIds.sublist(start, end).join("|")}");
|
||||||
Map<String, dynamic> result = jsonDecode(response.body);
|
Map<String, dynamic> result = jsonDecode(response.body);
|
||||||
Map<String, dynamic> batchEntities = result["entities"];
|
Map<String, dynamic> batchEntities = result["entities"];
|
||||||
entities.addAll(batchEntities);
|
entities.addAll(batchEntities);
|
||||||
|
@ -94,6 +94,12 @@ class WikidataMovieApi implements MovieApi {
|
||||||
// they will be retrieved from the cache in fromWikidataEntity
|
// they will be retrieved from the cache in fromWikidataEntity
|
||||||
await _getLabelsForEntities(allCountryAndGenreIds);
|
await _getLabelsForEntities(allCountryAndGenreIds);
|
||||||
|
|
||||||
|
// Get wikipedia explaintexts
|
||||||
|
Iterable<String> allWikipediaTitles =
|
||||||
|
selectInJson<String>(entities, "*.sitelinks.enwiki.url")
|
||||||
|
.map((url) => url.split("/").last);
|
||||||
|
await _getWikipediaExplainTextForTitles(allWikipediaTitles.toList());
|
||||||
|
|
||||||
return movieIds
|
return movieIds
|
||||||
.map((id) => WikidataMovieData.fromWikidataEntity(id, entities[id]))
|
.map((id) => WikidataMovieData.fromWikidataEntity(id, entities[id]))
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -117,83 +123,6 @@ class WikidataMovieApi implements MovieApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WikidataMovieData extends MovieData {
|
|
||||||
String entityId;
|
|
||||||
WikidataMovieData(
|
|
||||||
String title, DateWithPrecisionAndCountry releaseDate, this.entityId)
|
|
||||||
: super(title, releaseDate);
|
|
||||||
|
|
||||||
WikidataMovieData.fromEncodable(Map encodable)
|
|
||||||
: entityId = encodable["entityId"],
|
|
||||||
super.fromJsonEncodable(encodable);
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool same(MovieData other) {
|
|
||||||
return other is WikidataMovieData && entityId == other.entityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map toJsonEncodable() {
|
|
||||||
return super.toJsonEncodable()..addAll({"entityId": entityId});
|
|
||||||
}
|
|
||||||
|
|
||||||
static WikidataMovieData fromWikidataEntity(
|
|
||||||
String entityId, Map<String, dynamic> entity) {
|
|
||||||
String title =
|
|
||||||
selectInJson<String>(entity, "labels.en.value").firstOrNull ??
|
|
||||||
selectInJson<String>(entity, "labels.*.value").first;
|
|
||||||
Map<String, dynamic> claims = entity["claims"];
|
|
||||||
List<TitleInLanguage>? titles = selectInJson(
|
|
||||||
claims, "${WikidataProperties.title}.*.mainsnak.datavalue.value")
|
|
||||||
.map((value) => (
|
|
||||||
title: value["text"],
|
|
||||||
language: value["language"],
|
|
||||||
) as TitleInLanguage)
|
|
||||||
.toList();
|
|
||||||
List<DateWithPrecisionAndCountry> releaseDates =
|
|
||||||
_getReleaseDates(claims).toList();
|
|
||||||
// Sort release dates with higher precision to the beginning
|
|
||||||
releaseDates.sort((a, b) => -a.dateWithPrecision.precision.index
|
|
||||||
.compareTo(b.dateWithPrecision.precision.index));
|
|
||||||
List<String>? genres = selectInJson<String>(
|
|
||||||
claims, "${WikidataProperties.genre}.*.mainsnak.datavalue.value.id")
|
|
||||||
.map(_getCachedLabelForEntity)
|
|
||||||
.toList();
|
|
||||||
WikidataMovieData movie = WikidataMovieData(
|
|
||||||
title,
|
|
||||||
releaseDates.isNotEmpty
|
|
||||||
? releaseDates[0]
|
|
||||||
: DateWithPrecisionAndCountry(
|
|
||||||
DateTime.now(), DatePrecision.decade, "unknown location"),
|
|
||||||
entityId);
|
|
||||||
movie.setDetails(
|
|
||||||
titles: titles,
|
|
||||||
releaseDates: releaseDates,
|
|
||||||
genres: genres,
|
|
||||||
);
|
|
||||||
return movie;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Iterable<DateWithPrecisionAndCountry> _getReleaseDates(
|
|
||||||
Map<String, dynamic> claims) {
|
|
||||||
return selectInJson(claims, "${WikidataProperties.publicationDate}.*")
|
|
||||||
.where((dateClaim) => dateClaim["rank"] != "deprecated")
|
|
||||||
.expand<DateWithPrecisionAndCountry>((dateClaim) {
|
|
||||||
var value = selectInJson(dateClaim, "mainsnak.datavalue.value").first;
|
|
||||||
Iterable<String> countries = (selectInJson<String>(dateClaim,
|
|
||||||
"qualifiers.${WikidataProperties.placeOfPublication}.*.datavalue.value.id"))
|
|
||||||
.map(_getCachedLabelForEntity);
|
|
||||||
if (countries.isEmpty) {
|
|
||||||
countries = ["unknown location"];
|
|
||||||
}
|
|
||||||
return countries.map((country) => DateWithPrecisionAndCountry(
|
|
||||||
DateTime.parse(value["time"]),
|
|
||||||
_precisionFromWikidata(value["precision"]),
|
|
||||||
country));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _createUpcomingMovieQuery(
|
String _createUpcomingMovieQuery(
|
||||||
DateTime startDate, String instanceOf, int limit) {
|
DateTime startDate, String instanceOf, int limit) {
|
||||||
String date = DateFormat("yyyy-MM-dd").format(startDate);
|
String date = DateFormat("yyyy-MM-dd").format(startDate);
|
||||||
|
@ -213,7 +142,7 @@ ORDER BY ?minReleaseDate
|
||||||
LIMIT $limit""";
|
LIMIT $limit""";
|
||||||
}
|
}
|
||||||
|
|
||||||
DatePrecision _precisionFromWikidata(int precision) {
|
DatePrecision precisionFromWikidata(int precision) {
|
||||||
return switch (precision) {
|
return switch (precision) {
|
||||||
>= 13 => DatePrecision.minute,
|
>= 13 => DatePrecision.minute,
|
||||||
12 => DatePrecision.hour,
|
12 => DatePrecision.hour,
|
||||||
|
@ -264,6 +193,48 @@ Future<Map<String, String>> _getLabelsForEntities(
|
||||||
return labels;
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getCachedLabelForEntity(String entityId) {
|
String getCachedLabelForEntity(String entityId) {
|
||||||
return _labelCache[entityId] ?? entityId;
|
return _labelCache[entityId] ?? entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiManager _wikipediaApi =
|
||||||
|
ApiManager("https://en.wikipedia.org/w/api.php?format=json&origin=*");
|
||||||
|
Map<String, Dated<String?>> _wikipediaExplainTextCache = {};
|
||||||
|
|
||||||
|
Future<Map<String, Dated<String?>>> _getWikipediaExplainTextForTitles(
|
||||||
|
List<String> pageTitles) async {
|
||||||
|
const batchSize = 50;
|
||||||
|
Map<String, Dated<String?>> explainTexts = {};
|
||||||
|
for (int i = pageTitles.length - 1; i >= 0; i--) {
|
||||||
|
if (_wikipediaExplainTextCache.containsKey(pageTitles[i])) {
|
||||||
|
explainTexts[pageTitles[i]] = _wikipediaExplainTextCache[pageTitles[i]]!;
|
||||||
|
pageTitles.removeAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < (pageTitles.length / batchSize).ceil(); i++) {
|
||||||
|
final start = i * batchSize;
|
||||||
|
final end = min((i + 1) * batchSize, pageTitles.length);
|
||||||
|
Response response = await _wikipediaApi.get(
|
||||||
|
"&action=query&prop=extracts&exintro&explaintext&redirects=1&titles=${pageTitles.sublist(start, end).join("|")}");
|
||||||
|
Map<String, dynamic> result = jsonDecode(response.body);
|
||||||
|
List<dynamic> normalize = result["query"]["normalized"];
|
||||||
|
Map<String, dynamic> batchPages = result["query"]["pages"];
|
||||||
|
for (String pageId in batchPages.keys) {
|
||||||
|
String pageTitle = batchPages[pageId]["title"];
|
||||||
|
String originalTitle = normalize
|
||||||
|
.where((element) => element["to"] == pageTitle)
|
||||||
|
.firstOrNull?["from"] ??
|
||||||
|
pageTitle;
|
||||||
|
String? explainText = batchPages[pageId]["extract"];
|
||||||
|
if (explainText != null) {
|
||||||
|
_wikipediaExplainTextCache[originalTitle] =
|
||||||
|
explainTexts[originalTitle] = Dated.now(explainText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return explainTexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dated<String?>? getCachedWikipediaExplainTextFotTitle(String title) {
|
||||||
|
return _wikipediaExplainTextCache[title];
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:release_schedule/api/wikidata_movie_api.dart';
|
import 'package:release_schedule/api/wikidata/wikidata_movie.dart';
|
||||||
|
import 'package:release_schedule/api/wikidata/wikidata_movie_api.dart';
|
||||||
import 'package:release_schedule/model/dates.dart';
|
import 'package:release_schedule/model/dates.dart';
|
||||||
import 'package:release_schedule/model/live_search.dart';
|
import 'package:release_schedule/model/live_search.dart';
|
||||||
import 'package:release_schedule/model/local_movie_storage.dart';
|
import 'package:release_schedule/model/local_movie_storage.dart';
|
||||||
|
@ -17,6 +18,10 @@ class MyApp extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final MovieManager manager = MovieManager(
|
||||||
|
WikidataMovieApi(),
|
||||||
|
LocalMovieStorageGetStorage(WikidataMovieData.fromEncodable),
|
||||||
|
);
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Movie Schedule',
|
title: 'Movie Schedule',
|
||||||
themeMode: ThemeMode.dark,
|
themeMode: ThemeMode.dark,
|
||||||
|
@ -32,10 +37,7 @@ class MyApp extends StatelessWidget {
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: HomePage(
|
home: HomePage(manager),
|
||||||
MovieManager(WikidataMovieApi(),
|
|
||||||
LocalMovieStorageGetStorage(WikidataMovieData.fromEncodable)),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,9 +189,10 @@ class OverviewPage extends StatelessWidget {
|
||||||
// Only show movies that are bookmarked or have a release date with at least month precision and at least one title
|
// Only show movies that are bookmarked or have a release date with at least month precision and at least one title
|
||||||
filter: (movie) =>
|
filter: (movie) =>
|
||||||
movie.bookmarked ||
|
movie.bookmarked ||
|
||||||
(movie.releaseDate.dateWithPrecision.precision >=
|
(movie.releaseDate != null &&
|
||||||
|
movie.releaseDate!.dateWithPrecision.precision >=
|
||||||
DatePrecision.month &&
|
DatePrecision.month &&
|
||||||
(movie.titles?.length ?? 0) >= 1),
|
(movie.titles?.value?.length ?? 0) >= 1),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
child: const Icon(Icons.refresh),
|
child: const Icon(Icons.refresh),
|
||||||
|
@ -234,7 +237,7 @@ class HamburgerMenu extends StatelessWidget {
|
||||||
child: const Text("Remove irrelevant"),
|
child: const Text("Remove irrelevant"),
|
||||||
onTap: () => manager.removeMoviesWhere((movie) =>
|
onTap: () => manager.removeMoviesWhere((movie) =>
|
||||||
!movie.bookmarked &&
|
!movie.bookmarked &&
|
||||||
!(movie.releaseDates?.any((date) =>
|
!(movie.releaseDates?.value?.any((date) =>
|
||||||
date.dateWithPrecision.precision >=
|
date.dateWithPrecision.precision >=
|
||||||
DatePrecision.month &&
|
DatePrecision.month &&
|
||||||
date.dateWithPrecision.date.isAfter(DateTime.now()
|
date.dateWithPrecision.date.isAfter(DateTime.now()
|
||||||
|
|
|
@ -55,6 +55,7 @@ class DateWithPrecision implements Comparable<DateWithPrecision> {
|
||||||
.firstWhere((element) => element.name == json[1]);
|
.firstWhere((element) => element.name == json[1]);
|
||||||
|
|
||||||
DateWithPrecision.today() : this(DateTime.now().toUtc(), DatePrecision.day);
|
DateWithPrecision.today() : this(DateTime.now().toUtc(), DatePrecision.day);
|
||||||
|
DateWithPrecision.unspecified() : this(DateTime(0), DatePrecision.decade);
|
||||||
|
|
||||||
List<dynamic> toJsonEncodable() {
|
List<dynamic> toJsonEncodable() {
|
||||||
return [date.toIso8601String(), precision.name];
|
return [date.toIso8601String(), precision.name];
|
||||||
|
@ -122,3 +123,28 @@ class DateWithPrecision implements Comparable<DateWithPrecision> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Dated<T> {
|
||||||
|
final T value;
|
||||||
|
final DateTime date;
|
||||||
|
|
||||||
|
Dated(this.value, this.date);
|
||||||
|
|
||||||
|
Dated.now(this.value) : date = DateTime.now().toUtc();
|
||||||
|
|
||||||
|
Dated.fromJsonEncodable(
|
||||||
|
dynamic json, T Function(dynamic) valueFromJsonEncodable)
|
||||||
|
: value = valueFromJsonEncodable(json["value"]),
|
||||||
|
date = DateTime.parse(json["date"]);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJsonEncodable(
|
||||||
|
dynamic Function(T) valueToJsonEncodable) {
|
||||||
|
return {
|
||||||
|
"value": valueToJsonEncodable(value),
|
||||||
|
"date": date.toIso8601String()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
toString() => "$value as of $date";
|
||||||
|
}
|
||||||
|
|
|
@ -2,22 +2,26 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:release_schedule/model/dates.dart';
|
import 'package:release_schedule/model/dates.dart';
|
||||||
|
|
||||||
class MovieData extends ChangeNotifier {
|
class MovieData extends ChangeNotifier {
|
||||||
String _title;
|
|
||||||
DateWithPrecisionAndCountry _releaseDate;
|
|
||||||
bool _bookmarked = false;
|
bool _bookmarked = false;
|
||||||
|
|
||||||
bool _hasDetails = false;
|
String? _title;
|
||||||
List<DateWithPrecisionAndCountry>? _releaseDates;
|
DateWithPrecisionAndCountry? _releaseDate;
|
||||||
List<String>? _genres;
|
|
||||||
List<TitleInLanguage>? _titles;
|
|
||||||
|
|
||||||
MovieData(this._title, this._releaseDate);
|
// if it is entirely null the information was never loaded
|
||||||
|
// if only the value is null it was loaded but nothing was found
|
||||||
|
Dated<List<TextInLanguage>?>? _titles;
|
||||||
|
Dated<List<TextInLanguage>?>? _labels;
|
||||||
|
Dated<List<DateWithPrecisionAndCountry>?>? _releaseDates;
|
||||||
|
Dated<String?>? _description;
|
||||||
|
Dated<List<String>?>? _genres;
|
||||||
|
|
||||||
String get title {
|
MovieData();
|
||||||
|
|
||||||
|
String? get title {
|
||||||
return _title;
|
return _title;
|
||||||
}
|
}
|
||||||
|
|
||||||
DateWithPrecisionAndCountry get releaseDate {
|
DateWithPrecisionAndCountry? get releaseDate {
|
||||||
return _releaseDate;
|
return _releaseDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,111 +29,188 @@ class MovieData extends ChangeNotifier {
|
||||||
return _bookmarked;
|
return _bookmarked;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DateWithPrecisionAndCountry>? get releaseDates {
|
Dated<String?>? get description {
|
||||||
|
return _description;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dated<List<DateWithPrecisionAndCountry>?>? get releaseDates {
|
||||||
return _releaseDates;
|
return _releaseDates;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String>? get genres {
|
Dated<List<String>?>? get genres {
|
||||||
return _genres;
|
return _genres;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<TitleInLanguage>? get titles {
|
Dated<List<TextInLanguage>?>? get titles {
|
||||||
return _titles;
|
return _titles;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get hasDetails {
|
Dated<List<TextInLanguage>?>? get labels {
|
||||||
return _hasDetails;
|
return _labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the information with that of a new version of the movie
|
/// Updates the information with that of a new version of the movie
|
||||||
/// but ignores fields that are user controlled, like whether the movie was bookmarked.
|
/// but ignores fields that are user controlled, like whether the movie was bookmarked.
|
||||||
void updateWithNewIgnoringUserControlled(MovieData movie) {
|
void updateWithNewIgnoringUserControlled(MovieData movie) {
|
||||||
setDetails(
|
setDetails(
|
||||||
title: movie.title,
|
titles: movie.titles,
|
||||||
releaseDate: movie.releaseDate,
|
labels: movie.labels,
|
||||||
releaseDates: movie.releaseDates,
|
releaseDates: movie.releaseDates,
|
||||||
genres: movie.genres,
|
genres: movie.genres,
|
||||||
titles: movie.titles);
|
description: movie.description,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDetails(
|
void setNewDetails({
|
||||||
{String? title,
|
bool? bookmarked,
|
||||||
DateWithPrecisionAndCountry? releaseDate,
|
List<TextInLanguage>? titles,
|
||||||
bool? bookmarked,
|
List<TextInLanguage>? labels,
|
||||||
List<DateWithPrecisionAndCountry>? releaseDates,
|
List<DateWithPrecisionAndCountry>? releaseDates,
|
||||||
List<String>? genres,
|
List<String>? genres,
|
||||||
List<TitleInLanguage>? titles}) {
|
String? description,
|
||||||
if (title != null) {
|
}) {
|
||||||
_title = title;
|
setDetails(
|
||||||
}
|
bookmarked: bookmarked,
|
||||||
if (releaseDate != null) {
|
titles: titles != null ? Dated.now(titles) : null,
|
||||||
_releaseDate = releaseDate;
|
labels: labels != null ? Dated.now(labels) : null,
|
||||||
}
|
releaseDates: releaseDates != null ? Dated.now(releaseDates) : null,
|
||||||
|
genres: genres != null ? Dated.now(genres) : null,
|
||||||
|
description: description != null ? Dated.now(description) : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDetails({
|
||||||
|
bool? bookmarked,
|
||||||
|
Dated<List<TextInLanguage>?>? titles,
|
||||||
|
Dated<List<TextInLanguage>?>? labels,
|
||||||
|
Dated<List<DateWithPrecisionAndCountry>?>? releaseDates,
|
||||||
|
Dated<List<String>?>? genres,
|
||||||
|
Dated<String?>? description,
|
||||||
|
}) {
|
||||||
if (bookmarked != null) {
|
if (bookmarked != null) {
|
||||||
_bookmarked = bookmarked;
|
_bookmarked = bookmarked;
|
||||||
}
|
}
|
||||||
if (releaseDates != null) {
|
|
||||||
_releaseDates = releaseDates;
|
|
||||||
}
|
|
||||||
if (genres != null) {
|
|
||||||
_genres = genres;
|
|
||||||
}
|
|
||||||
if (titles != null) {
|
if (titles != null) {
|
||||||
_titles = titles;
|
_titles = titles;
|
||||||
}
|
}
|
||||||
_hasDetails = true;
|
if (labels != null) {
|
||||||
|
_labels = labels;
|
||||||
|
}
|
||||||
|
if (titles != null || labels != null) {
|
||||||
|
_title = null;
|
||||||
|
_title ??= _titles?.value
|
||||||
|
?.where((title) => title.language == "en")
|
||||||
|
.firstOrNull
|
||||||
|
?.text;
|
||||||
|
_title ??= _labels?.value
|
||||||
|
?.where((label) => label.language == "en")
|
||||||
|
.firstOrNull
|
||||||
|
?.text;
|
||||||
|
_title ??= _labels?.value?.firstOrNull?.text;
|
||||||
|
_title ??= _titles?.value?.firstOrNull?.text;
|
||||||
|
}
|
||||||
|
if (description != null) {
|
||||||
|
_description = description;
|
||||||
|
}
|
||||||
|
if (releaseDates != null) {
|
||||||
|
_releaseDates = releaseDates;
|
||||||
|
DateWithPrecisionAndCountry? mostPrecise =
|
||||||
|
_releaseDates?.value?.isNotEmpty ?? false
|
||||||
|
? _releaseDates?.value?.reduce((a, b) =>
|
||||||
|
a.dateWithPrecision.precision > b.dateWithPrecision.precision
|
||||||
|
? a
|
||||||
|
: b)
|
||||||
|
: null;
|
||||||
|
_releaseDate = mostPrecise;
|
||||||
|
}
|
||||||
|
if (genres != null) {
|
||||||
|
_genres = genres;
|
||||||
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "$title (${_releaseDate.toString()}${_genres?.isNotEmpty ?? false ? "; ${_genres?.join(", ")}" : ""})";
|
return "$title (${_releaseDate.toString()}${_genres?.value?.isNotEmpty ?? false ? "; ${_genres?.value?.join(", ")}" : ""})";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool same(MovieData other) {
|
bool same(MovieData other) {
|
||||||
return title == other.title &&
|
return title != null &&
|
||||||
releaseDate.dateWithPrecision == other.releaseDate.dateWithPrecision;
|
title == other.title &&
|
||||||
|
(releaseDate == null ||
|
||||||
|
other.releaseDate == null ||
|
||||||
|
releaseDate!.dateWithPrecision.date.year ==
|
||||||
|
other.releaseDate!.dateWithPrecision.date.year);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map toJsonEncodable() {
|
Map toJsonEncodable() {
|
||||||
List? releaseDatesByCountry =
|
dynamic releaseDatesByCountry = _releaseDates?.toJsonEncodable(
|
||||||
_releaseDates?.map((e) => e.toJsonEncodable()).toList();
|
(releaseDates) => releaseDates
|
||||||
List? titlesByCountry = _titles?.map((e) => [e.title, e.language]).toList();
|
?.map((releaseDate) => releaseDate.toJsonEncodable())
|
||||||
|
.toList());
|
||||||
|
dynamic titlesByCountry = _titles?.toJsonEncodable(
|
||||||
|
(titles) => titles?.map((e) => [e.text, e.language]).toList());
|
||||||
|
dynamic labels = _labels?.toJsonEncodable(
|
||||||
|
(labels) => labels?.map((e) => [e.text, e.language]).toList());
|
||||||
|
dynamic genres = _genres?.toJsonEncodable((genres) => genres);
|
||||||
return {
|
return {
|
||||||
"title": title,
|
|
||||||
"releaseDate": _releaseDate.toJsonEncodable(),
|
|
||||||
"bookmarked": _bookmarked,
|
"bookmarked": _bookmarked,
|
||||||
|
"titles": titlesByCountry,
|
||||||
|
"labels": labels,
|
||||||
"releaseDates": releaseDatesByCountry,
|
"releaseDates": releaseDatesByCountry,
|
||||||
"genres": genres,
|
"genres": genres,
|
||||||
"titles": titlesByCountry,
|
"description":
|
||||||
|
_description?.toJsonEncodable((description) => description),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieData.fromJsonEncodable(Map json)
|
MovieData.fromJsonEncodable(Map json) {
|
||||||
: _title = json["title"],
|
|
||||||
_releaseDate =
|
|
||||||
DateWithPrecisionAndCountry.fromJsonEncodable(json["releaseDate"]) {
|
|
||||||
setDetails(
|
setDetails(
|
||||||
bookmarked: json["bookmarked"] as bool,
|
bookmarked: json["bookmarked"] as bool? ?? false,
|
||||||
genres: (json["genres"] as List<dynamic>?)
|
titles: decodeOptionalJson<Dated<List<TextInLanguage>?>>(
|
||||||
?.map((genre) => genre as String)
|
json["titles"],
|
||||||
.toList(),
|
(json) => Dated.fromJsonEncodable(
|
||||||
releaseDates: json["releaseDates"] != null
|
json,
|
||||||
? (json["releaseDates"] as List<dynamic>)
|
(value) => (value as List<dynamic>)
|
||||||
.map((release) =>
|
.map((title) =>
|
||||||
DateWithPrecisionAndCountry.fromJsonEncodable(release))
|
(text: title[0], language: title[1]) as TextInLanguage)
|
||||||
.toList()
|
.toList())),
|
||||||
: null,
|
labels: decodeOptionalJson<Dated<List<TextInLanguage>?>>(
|
||||||
titles: json["titles"] != null
|
json["labels"],
|
||||||
? (json["titles"] as List<dynamic>)
|
(json) => Dated.fromJsonEncodable(
|
||||||
.map((title) =>
|
json,
|
||||||
(title: title[0], language: title[1]) as TitleInLanguage)
|
(value) => (value as List<dynamic>)
|
||||||
.toList()
|
.map((label) =>
|
||||||
: null);
|
(text: label[0], language: label[1]) as TextInLanguage)
|
||||||
|
.toList())),
|
||||||
|
genres: decodeOptionalJson<Dated<List<String>?>>(
|
||||||
|
json["genres"],
|
||||||
|
(json) =>
|
||||||
|
Dated.fromJsonEncodable(json, (value) => value.cast<String>())),
|
||||||
|
releaseDates:
|
||||||
|
decodeOptionalJson<Dated<List<DateWithPrecisionAndCountry>?>>(
|
||||||
|
json["releaseDates"],
|
||||||
|
(json) => Dated.fromJsonEncodable(
|
||||||
|
json,
|
||||||
|
(value) => (value as List<dynamic>)
|
||||||
|
.map((releaseDate) =>
|
||||||
|
DateWithPrecisionAndCountry.fromJsonEncodable(
|
||||||
|
releaseDate))
|
||||||
|
.toList())),
|
||||||
|
description: decodeOptionalJson<Dated<String?>>(json["description"],
|
||||||
|
(json) => Dated.fromJsonEncodable(json, (value) => value)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef TitleInLanguage = ({String title, String language});
|
T? decodeOptionalJson<T>(dynamic json, T Function(dynamic) decode) {
|
||||||
|
if (json == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return decode(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef TextInLanguage = ({String text, String language});
|
||||||
|
|
||||||
class DateWithPrecisionAndCountry {
|
class DateWithPrecisionAndCountry {
|
||||||
final DateWithPrecision dateWithPrecision;
|
final DateWithPrecision dateWithPrecision;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
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/model/dates.dart';
|
||||||
import 'package:release_schedule/model/delayed_function_caller.dart';
|
import 'package:release_schedule/model/delayed_function_caller.dart';
|
||||||
import 'package:release_schedule/model/local_movie_storage.dart';
|
import 'package:release_schedule/model/local_movie_storage.dart';
|
||||||
import 'package:release_schedule/model/movie.dart';
|
import 'package:release_schedule/model/movie.dart';
|
||||||
|
@ -64,11 +65,15 @@ class MovieManager extends ChangeNotifier {
|
||||||
void _insertMovie(MovieData movie) {
|
void _insertMovie(MovieData movie) {
|
||||||
int min = 0;
|
int min = 0;
|
||||||
int max = movies.length - 1;
|
int max = movies.length - 1;
|
||||||
|
DateWithPrecision? movieDate = movie.releaseDate?.dateWithPrecision;
|
||||||
while (min <= max) {
|
while (min <= max) {
|
||||||
int center = (min + max) ~/ 2;
|
int center = (min + max) ~/ 2;
|
||||||
int diff = movie.releaseDate.dateWithPrecision
|
DateWithPrecision? centerDate =
|
||||||
.compareTo(movies[center].releaseDate.dateWithPrecision);
|
movies[center].releaseDate?.dateWithPrecision;
|
||||||
if (diff < 0) {
|
int diff = movieDate != null && centerDate != null
|
||||||
|
? movieDate.compareTo(centerDate)
|
||||||
|
: 0;
|
||||||
|
if (movieDate == null || centerDate != null && diff < 0) {
|
||||||
max = center - 1;
|
max = center - 1;
|
||||||
} else {
|
} else {
|
||||||
min = center + 1;
|
min = center + 1;
|
||||||
|
@ -80,15 +85,13 @@ class MovieManager extends ChangeNotifier {
|
||||||
void _resortMovies() {
|
void _resortMovies() {
|
||||||
for (int i = 0; i < movies.length; i++) {
|
for (int i = 0; i < movies.length; i++) {
|
||||||
var temp = movies[i];
|
var temp = movies[i];
|
||||||
|
DateWithPrecision? tempDate = temp.releaseDate?.dateWithPrecision;
|
||||||
int j = i - 1;
|
int j = i - 1;
|
||||||
for (;
|
for (; j >= 0; j--) {
|
||||||
j >= 0 &&
|
DateWithPrecision? date = movies[j].releaseDate?.dateWithPrecision;
|
||||||
movies[j]
|
if (date == null || tempDate != null && date.compareTo(tempDate) <= 0) {
|
||||||
.releaseDate
|
break;
|
||||||
.dateWithPrecision
|
}
|
||||||
.compareTo(temp.releaseDate.dateWithPrecision) >
|
|
||||||
0;
|
|
||||||
j--) {
|
|
||||||
movies[j + 1] = movies[j];
|
movies[j + 1] = movies[j];
|
||||||
}
|
}
|
||||||
movies[j + 1] = temp;
|
movies[j + 1] = temp;
|
||||||
|
@ -115,8 +118,8 @@ class MovieManager extends ChangeNotifier {
|
||||||
movies,
|
movies,
|
||||||
search,
|
search,
|
||||||
(movie) => [
|
(movie) => [
|
||||||
movie.title,
|
movie.title ?? "",
|
||||||
...(movie.titles?.map((title) => title.title) ?? []),
|
...(movie.titles?.value?.map((title) => title.text) ?? []),
|
||||||
]);
|
]);
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,10 @@ class MovieItem extends StatelessWidget {
|
||||||
animation: movie,
|
animation: movie,
|
||||||
builder: (context, widget) {
|
builder: (context, widget) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(movie.title),
|
title: Text(movie.title ?? "-"),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
(showReleaseDate ? "${movie.releaseDate} " : "") +
|
(showReleaseDate ? "${movie.releaseDate} " : "") +
|
||||||
(movie.genres?.join(", ") ?? ""),
|
(movie.genres?.value?.join(", ") ?? ""),
|
||||||
),
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: Icon(movie.bookmarked
|
icon: Icon(movie.bookmarked
|
||||||
|
|
|
@ -78,9 +78,9 @@ class MovieList extends StatelessWidget {
|
||||||
int max = indexMap.length;
|
int max = indexMap.length;
|
||||||
while (min < max) {
|
while (min < max) {
|
||||||
int center = (min + max) ~/ 2;
|
int center = (min + max) ~/ 2;
|
||||||
DateWithPrecision date =
|
DateWithPrecision? date =
|
||||||
movies[indexMap[center]].releaseDate.dateWithPrecision;
|
movies[indexMap[center]].releaseDate?.dateWithPrecision;
|
||||||
if (date.compareTo(today) < 0) {
|
if (date != null && date.compareTo(today) < 0) {
|
||||||
min = center + 1;
|
min = center + 1;
|
||||||
} else {
|
} else {
|
||||||
max = center;
|
max = center;
|
||||||
|
@ -91,7 +91,8 @@ class MovieList extends StatelessWidget {
|
||||||
return GroupedList<DateWithPrecision>(
|
return GroupedList<DateWithPrecision>(
|
||||||
itemCount: indexMap.length,
|
itemCount: indexMap.length,
|
||||||
groupBy: (index) =>
|
groupBy: (index) =>
|
||||||
movies[indexMap[index]].releaseDate.dateWithPrecision,
|
movies[indexMap[index]].releaseDate?.dateWithPrecision ??
|
||||||
|
DateWithPrecision.unspecified(),
|
||||||
groupSeparatorBuilder: (date) => buildGroupSeparator(context, date),
|
groupSeparatorBuilder: (date) => buildGroupSeparator(context, date),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return MovieItem(movies[indexMap[index]]);
|
return MovieItem(movies[indexMap[index]]);
|
||||||
|
@ -106,8 +107,8 @@ class MovieList extends StatelessWidget {
|
||||||
int max = movies.length;
|
int max = movies.length;
|
||||||
while (min < max) {
|
while (min < max) {
|
||||||
int center = (min + max) ~/ 2;
|
int center = (min + max) ~/ 2;
|
||||||
DateWithPrecision date = movies[center].releaseDate.dateWithPrecision;
|
DateWithPrecision? date = movies[center].releaseDate?.dateWithPrecision;
|
||||||
if (date.compareTo(today) < 0) {
|
if (date != null && date.compareTo(today) < 0) {
|
||||||
min = center + 1;
|
min = center + 1;
|
||||||
} else {
|
} else {
|
||||||
max = center;
|
max = center;
|
||||||
|
@ -117,7 +118,9 @@ class MovieList extends StatelessWidget {
|
||||||
}();
|
}();
|
||||||
return GroupedList<DateWithPrecision>(
|
return GroupedList<DateWithPrecision>(
|
||||||
itemCount: movies.length,
|
itemCount: movies.length,
|
||||||
groupBy: (index) => movies[index].releaseDate.dateWithPrecision,
|
groupBy: (index) =>
|
||||||
|
movies[index].releaseDate?.dateWithPrecision ??
|
||||||
|
DateWithPrecision.unspecified(),
|
||||||
groupSeparatorBuilder: (date) => buildGroupSeparator(context, date),
|
groupSeparatorBuilder: (date) => buildGroupSeparator(context, date),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return MovieItem(movies[index]);
|
return MovieItem(movies[index]);
|
||||||
|
|
|
@ -29,7 +29,7 @@ class MoviePage extends StatelessWidget {
|
||||||
animation: movie,
|
animation: movie,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(movie.title), actions: [
|
appBar: AppBar(title: Text(movie.title ?? "-"), actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(movie.bookmarked
|
icon: Icon(movie.bookmarked
|
||||||
? Icons.bookmark_added
|
? Icons.bookmark_added
|
||||||
|
@ -39,19 +39,27 @@ class MoviePage extends StatelessWidget {
|
||||||
]),
|
]),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(18.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
runSpacing: 10,
|
runSpacing: 10,
|
||||||
children: movie.genres
|
children: movie.genres?.value
|
||||||
?.map((genre) => Chip(label: Text(genre)))
|
?.map((genre) => Chip(label: Text(genre)))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const Heading("About"),
|
||||||
|
Text(
|
||||||
|
movie.description?.value?.trim().replaceAll("\n", "\n\n") ??
|
||||||
|
"-",
|
||||||
|
textAlign: TextAlign.justify,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
height: 1.6,
|
||||||
|
),
|
||||||
|
),
|
||||||
const Heading("Titles"),
|
const Heading("Titles"),
|
||||||
Table(
|
Table(
|
||||||
border: TableBorder.symmetric(
|
border: TableBorder.symmetric(
|
||||||
|
@ -59,7 +67,7 @@ class MoviePage extends StatelessWidget {
|
||||||
color: Theme.of(context).dividerColor,
|
color: Theme.of(context).dividerColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
children: movie.titles?.map((title) {
|
children: movie.titles?.value?.map((title) {
|
||||||
return TableRow(
|
return TableRow(
|
||||||
children: [
|
children: [
|
||||||
TableCell(
|
TableCell(
|
||||||
|
@ -70,7 +78,7 @@ class MoviePage extends StatelessWidget {
|
||||||
TableCell(
|
TableCell(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(title.title),
|
child: Text(title.text),
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -84,7 +92,7 @@ class MoviePage extends StatelessWidget {
|
||||||
color: Theme.of(context).dividerColor,
|
color: Theme.of(context).dividerColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
children: movie.releaseDates?.map((releaseDate) {
|
children: movie.releaseDates?.value?.map((releaseDate) {
|
||||||
return TableRow(
|
return TableRow(
|
||||||
children: [
|
children: [
|
||||||
TableCell(
|
TableCell(
|
||||||
|
|
|
@ -9,6 +9,23 @@ void main() {
|
||||||
group('MovieManager', () {
|
group('MovieManager', () {
|
||||||
late MovieManager movieManager;
|
late MovieManager movieManager;
|
||||||
|
|
||||||
|
final theMatrix = MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'The Matrix', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(1999, 3, 31), DatePrecision.day, 'USA')
|
||||||
|
],
|
||||||
|
);
|
||||||
|
final theMatrixReloaded = MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'The Matrix Reloaded', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2003, 5, 7), DatePrecision.day, 'USA')
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
movieManager = MovieManager(
|
movieManager = MovieManager(
|
||||||
MovieApi(),
|
MovieApi(),
|
||||||
|
@ -18,16 +35,8 @@ void main() {
|
||||||
|
|
||||||
test('addMovies should add movies to the list', () {
|
test('addMovies should add movies to the list', () {
|
||||||
final movies = [
|
final movies = [
|
||||||
MovieData(
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrix),
|
||||||
'The Matrix',
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrixReloaded),
|
||||||
DateWithPrecisionAndCountry(DateTime(1999, 3, 31), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
MovieData(
|
|
||||||
'The Matrix Reloaded',
|
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
movieManager.addMovies(movies);
|
movieManager.addMovies(movies);
|
||||||
|
@ -37,56 +46,45 @@ void main() {
|
||||||
|
|
||||||
test('addMovies should add new movies', () {
|
test('addMovies should add new movies', () {
|
||||||
final movies = [
|
final movies = [
|
||||||
MovieData(
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrix),
|
||||||
'The Matrix',
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrixReloaded),
|
||||||
DateWithPrecisionAndCountry(DateTime(1999, 3, 31), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
MovieData(
|
|
||||||
'The Matrix Reloaded',
|
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
movieManager.addMovies(movies);
|
movieManager.addMovies(movies);
|
||||||
|
|
||||||
final newMovies = [
|
final newMovies = [
|
||||||
MovieData(
|
MovieData()
|
||||||
'The Matrix Revolutions',
|
..setNewDetails(
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 11, 5), DatePrecision.day,
|
labels: [(text: 'The Matrix Revolutions', language: 'en')],
|
||||||
'United States of America'),
|
releaseDates: [
|
||||||
),
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2003, 11, 5), DatePrecision.day, 'USA')
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
movieManager.addMovies(newMovies);
|
movieManager.addMovies(newMovies);
|
||||||
|
|
||||||
expect(movieManager.movies, equals([...movies, ...newMovies]));
|
expect(movieManager.movies, equals(movies + newMovies));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('addMovies should update existing movies', () {
|
test('addMovies should update existing movies', () {
|
||||||
final movies = [
|
final movies = [
|
||||||
MovieData(
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrix),
|
||||||
'The Matrix',
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrixReloaded),
|
||||||
DateWithPrecisionAndCountry(DateTime(1999, 3, 31), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
MovieData(
|
|
||||||
'The Matrix Reloaded',
|
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
movieManager.addMovies(movies);
|
movieManager.addMovies(movies);
|
||||||
|
|
||||||
final updatedMovie = MovieData(
|
final updatedMovie = MovieData()
|
||||||
'The Matrix Reloaded',
|
..setNewDetails(
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
)..setDetails(
|
|
||||||
bookmarked: true,
|
bookmarked: true,
|
||||||
genres: ['Action', 'Adventure'],
|
genres: ['Action', 'Adventure'],
|
||||||
|
labels: [(text: 'The Matrix Reloaded', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2003, 5, 7), DatePrecision.day, 'USA')
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
movieManager.addMovies([updatedMovie]);
|
movieManager.addMovies([updatedMovie]);
|
||||||
|
@ -97,37 +95,35 @@ void main() {
|
||||||
|
|
||||||
test('addMovies should sort movies by their release dates', () {
|
test('addMovies should sort movies by their release dates', () {
|
||||||
final movies = [
|
final movies = [
|
||||||
MovieData(
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrixReloaded),
|
||||||
'The Matrix Reloaded',
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrix),
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
MovieData(
|
|
||||||
'The Matrix',
|
|
||||||
DateWithPrecisionAndCountry(DateTime(1999, 3, 31), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
movieManager.addMovies(movies);
|
movieManager.addMovies(movies);
|
||||||
|
|
||||||
expect(movieManager.movies, equals([...movies.reversed]));
|
expect(movieManager.movies, equals(movies.reversed.toList()));
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'addMovies should sort movies that have a less precise release date before movies with more precise release dates',
|
'addMovies should sort movies that have a less precise release date before movies with more precise release dates',
|
||||||
() {
|
() {
|
||||||
final movies = [
|
final movies = [
|
||||||
MovieData(
|
MovieData()
|
||||||
'The Matrix Reloaded',
|
..updateWithNewIgnoringUserControlled(theMatrixReloaded)
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
..setNewDetails(
|
||||||
'United States of America'),
|
releaseDates: [
|
||||||
),
|
DateWithPrecisionAndCountry(
|
||||||
MovieData(
|
DateTime(2003, 5, 7), DatePrecision.day, 'USA')
|
||||||
'The Matrix',
|
],
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.month,
|
),
|
||||||
'United States of America'),
|
MovieData()
|
||||||
),
|
..updateWithNewIgnoringUserControlled(theMatrix)
|
||||||
|
..setNewDetails(
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2003, 5, 7), DatePrecision.month, 'USA')
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
movieManager.addMovies(movies);
|
movieManager.addMovies(movies);
|
||||||
|
@ -139,68 +135,59 @@ void main() {
|
||||||
'when a movie is modified and it\'s date is changed the movies should be resorted',
|
'when a movie is modified and it\'s date is changed the movies should be resorted',
|
||||||
() async {
|
() async {
|
||||||
final movies = [
|
final movies = [
|
||||||
MovieData(
|
MovieData()
|
||||||
'The Matrix Reloaded',
|
..updateWithNewIgnoringUserControlled(theMatrixReloaded)
|
||||||
DateWithPrecisionAndCountry(DateTime(1998, 5, 7), DatePrecision.day,
|
..setNewDetails(
|
||||||
'United States of America'),
|
releaseDates: [
|
||||||
),
|
DateWithPrecisionAndCountry(
|
||||||
MovieData(
|
DateTime(1998, 5, 7), DatePrecision.day, 'USA')
|
||||||
'The Matrix',
|
],
|
||||||
DateWithPrecisionAndCountry(DateTime(1999, 3, 31), DatePrecision.day,
|
),
|
||||||
'United States of America'),
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrix),
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
movieManager.addMovies(movies);
|
movieManager.addMovies(movies);
|
||||||
|
|
||||||
final movie = movieManager.movies.first;
|
final movie = movieManager.movies.first;
|
||||||
movie.setDetails(
|
movie.setNewDetails(
|
||||||
releaseDate: DateWithPrecisionAndCountry(DateTime(2003, 5, 7),
|
releaseDates: [
|
||||||
DatePrecision.day, 'United States of America'),
|
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
||||||
|
'United States of America')
|
||||||
|
],
|
||||||
);
|
);
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
expect(movieManager.movies, equals([...movies.reversed]));
|
expect(movieManager.movies, equals(movies.reversed.toList()));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('removeMoviesWhere should remove movies from the list', () {
|
test('removeMoviesWhere should remove movies from the list', () {
|
||||||
final movies = [
|
final movies = [
|
||||||
MovieData(
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrix),
|
||||||
'The Matrix',
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrixReloaded),
|
||||||
DateWithPrecisionAndCountry(DateTime(1999, 3, 31), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
MovieData(
|
|
||||||
'The Matrix Reloaded',
|
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
MovieData notRemoved = MovieData(
|
MovieData notRemoved = MovieData()
|
||||||
'Harry Potter and the Philosopher\'s Stone',
|
..setNewDetails(
|
||||||
DateWithPrecisionAndCountry(
|
labels: [
|
||||||
DateTime(2001, 11, 4), DatePrecision.day, 'United Kingdom'),
|
(text: 'Harry Potter and the Philosopher\'s Stone', language: 'en')
|
||||||
);
|
],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2001, 11, 4), DatePrecision.day, 'UK')
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
movieManager.addMovies([...movies, notRemoved]);
|
movieManager.addMovies(movies + [notRemoved]);
|
||||||
|
|
||||||
movieManager.removeMoviesWhere((movie) => movie.title.contains('Matrix'));
|
movieManager.removeMoviesWhere(
|
||||||
|
(movie) => movie.title?.contains('Matrix') == true);
|
||||||
|
|
||||||
expect(movieManager.movies, equals([notRemoved]));
|
expect(movieManager.movies, equals([notRemoved]));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("localSearch", () {
|
test("localSearch", () {
|
||||||
final movies = [
|
final movies = [
|
||||||
MovieData(
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrix),
|
||||||
'The Matrix',
|
MovieData()..updateWithNewIgnoringUserControlled(theMatrixReloaded),
|
||||||
DateWithPrecisionAndCountry(DateTime(1999, 3, 31), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
MovieData(
|
|
||||||
'The Matrix Reloaded',
|
|
||||||
DateWithPrecisionAndCountry(DateTime(2003, 5, 7), DatePrecision.day,
|
|
||||||
'United States of America'),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
movieManager.addMovies(movies);
|
movieManager.addMovies(movies);
|
||||||
|
|
|
@ -4,114 +4,108 @@ import 'package:release_schedule/model/movie.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('MovieData', () {
|
group('MovieData', () {
|
||||||
|
MovieData firstMovie = MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'Title 1', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
);
|
||||||
|
MovieData secondMovie = MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'Title 2', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
test('updateWithNew() updates all fields', () {
|
test('updateWithNew() updates all fields', () {
|
||||||
final movie1 = MovieData(
|
final movie1 = MovieData()
|
||||||
'Title 1',
|
..updateWithNewIgnoringUserControlled(firstMovie);
|
||||||
DateWithPrecisionAndCountry(
|
final movie2 = MovieData()
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'));
|
..updateWithNewIgnoringUserControlled(secondMovie)
|
||||||
final movie2 = MovieData(
|
..setNewDetails(
|
||||||
'Title 2',
|
releaseDates: [
|
||||||
DateWithPrecisionAndCountry(
|
DateWithPrecisionAndCountry(
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'UK'));
|
DateTime(2023, 1, 1), DatePrecision.day, 'UK')
|
||||||
movie2.setDetails(releaseDates: [
|
],
|
||||||
DateWithPrecisionAndCountry(
|
genres: ['Action', 'Adventure'],
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
titles: [(text: 'Titel 2', language: 'de')],
|
||||||
], genres: [
|
);
|
||||||
'Action',
|
|
||||||
'Adventure'
|
|
||||||
], titles: [
|
|
||||||
(title: 'Title 2', language: 'en')
|
|
||||||
]);
|
|
||||||
movie1.updateWithNewIgnoringUserControlled(movie2);
|
movie1.updateWithNewIgnoringUserControlled(movie2);
|
||||||
expect(movie1.title, equals('Title 2'));
|
expect(movie1.title, equals('Title 2'));
|
||||||
expect(movie1.releaseDate.country, equals('UK'));
|
expect(movie1.releaseDate?.country, equals('UK'));
|
||||||
expect(movie1.releaseDates!.length, equals(1));
|
expect(movie1.releaseDates?.value?.length, equals(1));
|
||||||
expect(movie1.releaseDates![0].country, equals('US'));
|
expect(movie1.releaseDates?.value?[0].country, equals('UK'));
|
||||||
expect(movie1.genres!.length, equals(2));
|
expect(movie1.genres?.value?.length, equals(2));
|
||||||
expect(movie1.genres![0], equals('Action'));
|
expect(movie1.genres?.value?[0], equals('Action'));
|
||||||
expect(movie1.genres![1], equals('Adventure'));
|
expect(movie1.genres?.value?[1], equals('Adventure'));
|
||||||
expect(movie1.titles!.length, equals(1));
|
expect(movie1.titles?.value?.length, equals(1));
|
||||||
expect(movie1.titles![0].title, equals('Title 2'));
|
expect(movie1.titles?.value?[0].text, equals('Titel 2'));
|
||||||
expect(movie1.titles![0].language, equals('en'));
|
expect(movie1.titles?.value?[0].language, equals('de'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('same() returns true for same title and release date', () {
|
test('same() returns true for same title and release year', () {
|
||||||
final movie1 = MovieData(
|
final movie1 = MovieData()
|
||||||
'Title 1',
|
..updateWithNewIgnoringUserControlled(firstMovie);
|
||||||
DateWithPrecisionAndCountry(
|
final movie2 = MovieData()
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'));
|
..updateWithNewIgnoringUserControlled(firstMovie)
|
||||||
final movie2 = MovieData(
|
..setNewDetails(
|
||||||
'Title 1',
|
releaseDates: [
|
||||||
DateWithPrecisionAndCountry(
|
DateWithPrecisionAndCountry(
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'));
|
DateTime(2023, 4, 27), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
);
|
||||||
expect(movie1.same(movie2), isTrue);
|
expect(movie1.same(movie2), isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('same() returns false for different title', () {
|
test('same() returns false for different title', () {
|
||||||
final movie1 = MovieData(
|
final movie1 = MovieData()
|
||||||
'Title 1',
|
..updateWithNewIgnoringUserControlled(firstMovie);
|
||||||
DateWithPrecisionAndCountry(
|
final movie2 = MovieData()
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'));
|
..updateWithNewIgnoringUserControlled(secondMovie);
|
||||||
final movie2 = MovieData(
|
|
||||||
'Title 2',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'));
|
|
||||||
expect(movie1.same(movie2), isFalse);
|
expect(movie1.same(movie2), isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('same() returns false for different release date', () {
|
test('same() returns false for different release years', () {
|
||||||
final movie1 = MovieData(
|
final movie1 = MovieData()
|
||||||
'Title 1',
|
..updateWithNewIgnoringUserControlled(firstMovie);
|
||||||
DateWithPrecisionAndCountry(
|
final movie2 = MovieData()
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'));
|
..updateWithNewIgnoringUserControlled(firstMovie)
|
||||||
final movie2 = MovieData(
|
..setNewDetails(
|
||||||
'Title 1',
|
releaseDates: [
|
||||||
DateWithPrecisionAndCountry(
|
DateWithPrecisionAndCountry(
|
||||||
DateTime(2023, 1, 2), DatePrecision.day, 'US'));
|
DateTime(2022, 1, 1), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
);
|
||||||
expect(movie1.same(movie2), isFalse);
|
expect(movie1.same(movie2), isFalse);
|
||||||
});
|
});
|
||||||
test('can be encoded to JSON and back', () {
|
test('can be encoded to JSON and back', () {
|
||||||
final movie = MovieData(
|
final movie = MovieData()
|
||||||
'Title 1',
|
..updateWithNewIgnoringUserControlled(firstMovie)
|
||||||
DateWithPrecisionAndCountry(
|
..setNewDetails(
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'));
|
genres: ['Action', 'Adventure'],
|
||||||
movie.setDetails(releaseDates: [
|
);
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
|
||||||
], genres: [
|
|
||||||
'Action',
|
|
||||||
'Adventure'
|
|
||||||
], titles: [
|
|
||||||
(title: 'Title 2', language: 'en')
|
|
||||||
]);
|
|
||||||
final json = movie.toJsonEncodable();
|
final json = movie.toJsonEncodable();
|
||||||
final movie2 = MovieData.fromJsonEncodable(json);
|
final movie2 = MovieData.fromJsonEncodable(json);
|
||||||
expect(movie2.title, equals('Title 1'));
|
expect(movie2.title, equals('Title 1'));
|
||||||
expect(movie2.releaseDate.country, equals('US'));
|
expect(movie2.releaseDate?.country, equals('US'));
|
||||||
expect(movie2.releaseDates!.length, equals(1));
|
expect(movie2.releaseDates?.value?.length, equals(1));
|
||||||
expect(movie2.releaseDates![0].country, equals('US'));
|
expect(movie2.releaseDates?.value?[0].country, equals('US'));
|
||||||
expect(movie2.genres!.length, equals(2));
|
expect(movie2.genres?.value?.length, equals(2));
|
||||||
expect(movie2.genres![0], equals('Action'));
|
expect(movie2.genres?.value?[0], equals('Action'));
|
||||||
expect(movie2.genres![1], equals('Adventure'));
|
expect(movie2.genres?.value?[1], equals('Adventure'));
|
||||||
expect(movie2.titles!.length, equals(1));
|
expect(movie2.titles, equals(null));
|
||||||
expect(movie2.titles![0].title, equals('Title 2'));
|
|
||||||
expect(movie2.titles![0].language, equals('en'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('toString()', () {
|
test('toString()', () {
|
||||||
final movie = MovieData(
|
final movie = MovieData()
|
||||||
'Title 1',
|
..updateWithNewIgnoringUserControlled(firstMovie)
|
||||||
DateWithPrecisionAndCountry(
|
..setNewDetails(
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'));
|
genres: ['Action', 'Adventure'],
|
||||||
movie.setDetails(releaseDates: [
|
);
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
|
||||||
], genres: [
|
|
||||||
'Action',
|
|
||||||
'Adventure'
|
|
||||||
], titles: [
|
|
||||||
(title: 'Title 2', language: 'en')
|
|
||||||
]);
|
|
||||||
expect(movie.toString(),
|
expect(movie.toString(),
|
||||||
equals('Title 1 (January 1, 2023 (US); Action, Adventure)'));
|
equals('Title 1 (January 1, 2023 (US); Action, Adventure)'));
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,21 +15,30 @@ void main() {
|
||||||
setUp(() {
|
setUp(() {
|
||||||
storage = InMemoryMovieStorage();
|
storage = InMemoryMovieStorage();
|
||||||
storage.update([
|
storage.update([
|
||||||
MovieData(
|
MovieData()
|
||||||
'The Shawshank Redemption',
|
..setNewDetails(
|
||||||
DateWithPrecisionAndCountry(
|
labels: [(text: 'The Shawshank Redemption', language: 'en')],
|
||||||
DateTime(1994, 9, 22), DatePrecision.day, 'US'),
|
releaseDates: [
|
||||||
),
|
DateWithPrecisionAndCountry(
|
||||||
MovieData(
|
DateTime(1994, 9, 22), DatePrecision.day, 'US')
|
||||||
'The Godfather',
|
],
|
||||||
DateWithPrecisionAndCountry(
|
),
|
||||||
DateTime(1972, 3, 24), DatePrecision.day, 'US'),
|
MovieData()
|
||||||
),
|
..setNewDetails(
|
||||||
MovieData(
|
labels: [(text: 'The Godfather', language: 'en')],
|
||||||
'The Dark Knight',
|
releaseDates: [
|
||||||
DateWithPrecisionAndCountry(
|
DateWithPrecisionAndCountry(
|
||||||
DateTime(2008, 7, 18), DatePrecision.day, 'US'),
|
DateTime(1972, 3, 24), DatePrecision.day, 'US')
|
||||||
),
|
],
|
||||||
|
),
|
||||||
|
MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'The Dark Knight', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2008, 7, 18), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,26 @@ import 'package:release_schedule/view/movie_item.dart';
|
||||||
import 'package:release_schedule/view/movie_page.dart';
|
import 'package:release_schedule/view/movie_page.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
late MovieData testMovie;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
testMovie = MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'Test Movie', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
testWidgets('MovieItem displays movie data', (WidgetTester tester) async {
|
testWidgets('MovieItem displays movie data', (WidgetTester tester) async {
|
||||||
final movie = MovieData(
|
testMovie.setNewDetails(
|
||||||
'Test Movie',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'),
|
|
||||||
);
|
|
||||||
movie.setDetails(
|
|
||||||
genres: ['Action', 'Adventure'],
|
genres: ['Action', 'Adventure'],
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: MovieItem(movie),
|
body: MovieItem(testMovie),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -30,26 +36,17 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('should update when the movie is modified', (tester) async {
|
testWidgets('should update when the movie is modified', (tester) async {
|
||||||
final movie = MovieData(
|
|
||||||
'Test Movie',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'),
|
|
||||||
);
|
|
||||||
movie.setDetails(
|
|
||||||
genres: ['Action', 'Adventure'],
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: MovieItem(movie),
|
body: MovieItem(testMovie),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(find.text('Test Movie'), findsOneWidget);
|
expect(find.text('Test Movie'), findsOneWidget);
|
||||||
|
|
||||||
movie.setDetails(
|
testMovie.setNewDetails(
|
||||||
genres: ['Action', 'Adventure', 'Comedy'],
|
genres: ['Action', 'Adventure', 'Comedy'],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -59,26 +56,17 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('should update when the movie is bookmarked', (tester) async {
|
testWidgets('should update when the movie is bookmarked', (tester) async {
|
||||||
final movie = MovieData(
|
|
||||||
'Test Movie',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'),
|
|
||||||
);
|
|
||||||
movie.setDetails(
|
|
||||||
genres: ['Action', 'Adventure'],
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: MovieItem(movie),
|
body: MovieItem(testMovie),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(find.byIcon(Icons.bookmark_border), findsOneWidget);
|
expect(find.byIcon(Icons.bookmark_border), findsOneWidget);
|
||||||
|
|
||||||
movie.setDetails(
|
testMovie.setDetails(
|
||||||
bookmarked: true,
|
bookmarked: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -89,19 +77,10 @@ void main() {
|
||||||
|
|
||||||
testWidgets("should update the bookmark state when the icon is tapped",
|
testWidgets("should update the bookmark state when the icon is tapped",
|
||||||
(tester) async {
|
(tester) async {
|
||||||
final movie = MovieData(
|
|
||||||
'Test Movie',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'),
|
|
||||||
);
|
|
||||||
movie.setDetails(
|
|
||||||
genres: ['Action', 'Adventure'],
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: MovieItem(movie),
|
body: MovieItem(testMovie),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -116,19 +95,10 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("should navigate to MoviePage when tapped", (tester) async {
|
testWidgets("should navigate to MoviePage when tapped", (tester) async {
|
||||||
final movie = MovieData(
|
|
||||||
'Test Movie',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US'),
|
|
||||||
);
|
|
||||||
movie.setDetails(
|
|
||||||
genres: ['Action', 'Adventure'],
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: MovieItem(movie),
|
body: MovieItem(testMovie),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,20 +7,30 @@ import 'package:release_schedule/view/movie_list.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('MovieList', () {
|
group('MovieList', () {
|
||||||
testWidgets('should render a list of movies', (WidgetTester tester) async {
|
late List<MovieData> movies;
|
||||||
final movies = [
|
|
||||||
MovieData(
|
|
||||||
'The Shawshank Redemption',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(1994, 9, 22), DatePrecision.day, 'US'),
|
|
||||||
),
|
|
||||||
MovieData(
|
|
||||||
'The Godfather',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(1972, 3, 24), DatePrecision.day, 'US'),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
movies = [
|
||||||
|
MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'The Shawshank Redemption', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(1994, 9, 22), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'The Godfather', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(1972, 3, 24), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
)
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('should render a list of movies', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
|
@ -36,25 +46,12 @@ void main() {
|
||||||
|
|
||||||
testWidgets("should filter the list of movies",
|
testWidgets("should filter the list of movies",
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final movies = [
|
|
||||||
MovieData(
|
|
||||||
'The Shawshank Redemption',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(1994, 9, 22), DatePrecision.day, 'US'),
|
|
||||||
),
|
|
||||||
MovieData(
|
|
||||||
'The Godfather',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(1972, 3, 24), DatePrecision.day, 'US'),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: MovieList(
|
body: MovieList(
|
||||||
movies,
|
movies,
|
||||||
filter: (movie) => movie.title.contains('Godfather'),
|
filter: (movie) => movie.title?.contains('Godfather') == true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -11,18 +11,31 @@ import 'package:release_schedule/view/movie_manager_list.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('MovieManagerList', () {
|
group('MovieManagerList', () {
|
||||||
|
late List<MovieData> movies;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
movies = [
|
||||||
|
MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'Movie 1', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'Movie 2', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
)
|
||||||
|
];
|
||||||
|
});
|
||||||
testWidgets('displays movie list', (tester) async {
|
testWidgets('displays movie list', (tester) async {
|
||||||
final manager = MovieManager(MovieApi(), InMemoryMovieStorage());
|
final manager = MovieManager(MovieApi(), InMemoryMovieStorage());
|
||||||
manager.addMovies([
|
manager.addMovies(movies);
|
||||||
MovieData(
|
|
||||||
'Movie 1',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US')),
|
|
||||||
MovieData(
|
|
||||||
'Movie 2',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US')),
|
|
||||||
]);
|
|
||||||
// pump the delay until the change is written to the cache, so no timers run when the test finishes
|
// pump the delay until the change is written to the cache, so no timers run when the test finishes
|
||||||
await tester.pump(const Duration(seconds: 5));
|
await tester.pump(const Duration(seconds: 5));
|
||||||
|
|
||||||
|
@ -35,25 +48,20 @@ void main() {
|
||||||
|
|
||||||
testWidgets('updates when new movies are added', (tester) async {
|
testWidgets('updates when new movies are added', (tester) async {
|
||||||
final manager = MovieManager(MovieApi(), InMemoryMovieStorage());
|
final manager = MovieManager(MovieApi(), InMemoryMovieStorage());
|
||||||
manager.addMovies([
|
manager.addMovies(movies);
|
||||||
MovieData(
|
|
||||||
'Movie 1',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US')),
|
|
||||||
MovieData(
|
|
||||||
'Movie 2',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US')),
|
|
||||||
]);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(home: Scaffold(body: MovieManagerList(manager))));
|
MaterialApp(home: Scaffold(body: MovieManagerList(manager))));
|
||||||
|
|
||||||
manager.addMovies([
|
manager.addMovies([
|
||||||
MovieData(
|
MovieData()
|
||||||
'Movie 3',
|
..setNewDetails(
|
||||||
DateWithPrecisionAndCountry(
|
labels: [(text: 'Movie 3', language: 'en')],
|
||||||
DateTime(2023, 1, 1), DatePrecision.day, 'US')),
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(2023, 1, 1), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
)
|
||||||
]);
|
]);
|
||||||
// pump the delay until the change is written to the cache, so no timers run when the test finishes
|
// pump the delay until the change is written to the cache, so no timers run when the test finishes
|
||||||
await tester.pump(const Duration(seconds: 5));
|
await tester.pump(const Duration(seconds: 5));
|
||||||
|
|
|
@ -6,13 +6,20 @@ import 'package:release_schedule/view/movie_page.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('MoviePage', () {
|
group('MoviePage', () {
|
||||||
testWidgets('should render the movie details', (WidgetTester tester) async {
|
late MovieData movie;
|
||||||
final movie = MovieData(
|
|
||||||
'The Shawshank Redemption',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(1994, 9, 22), DatePrecision.day, 'US'),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
movie = MovieData()
|
||||||
|
..setNewDetails(
|
||||||
|
labels: [(text: 'The Shawshank Redemption', language: 'en')],
|
||||||
|
releaseDates: [
|
||||||
|
DateWithPrecisionAndCountry(
|
||||||
|
DateTime(1994, 9, 22), DatePrecision.day, 'US')
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('should render the movie details', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
|
@ -23,16 +30,10 @@ void main() {
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.text(movie.title), findsAtLeastNWidgets(1));
|
expect(find.text(movie.title!), findsAtLeastNWidgets(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('should bookmark the movie', (WidgetTester tester) async {
|
testWidgets('should bookmark the movie', (WidgetTester tester) async {
|
||||||
final movie = MovieData(
|
|
||||||
'The Shawshank Redemption',
|
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(1994, 9, 22), DatePrecision.day, 'US'),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
|
@ -50,56 +51,48 @@ void main() {
|
||||||
|
|
||||||
expect(movie.bookmarked, isTrue);
|
expect(movie.bookmarked, isTrue);
|
||||||
});
|
});
|
||||||
});
|
testWidgets("should display the movie's genres",
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
movie.setNewDetails(genres: ['Drama']);
|
||||||
|
|
||||||
testWidgets("should display the movie's genres", (WidgetTester tester) async {
|
await tester.pumpWidget(
|
||||||
final movie = MovieData(
|
MaterialApp(
|
||||||
'The Shawshank Redemption',
|
home: Scaffold(
|
||||||
DateWithPrecisionAndCountry(
|
body: MoviePage(movie),
|
||||||
DateTime(1994, 9, 22), DatePrecision.day, 'US'),
|
),
|
||||||
)..setDetails(genres: ['Drama']);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: MoviePage(movie),
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.text('Drama'), findsOneWidget);
|
expect(find.text('Drama'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("should display the movie's titles and release dates",
|
testWidgets("should display the movie's titles and release dates",
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final movie = MovieData(
|
movie.setNewDetails(
|
||||||
'The Shawshank Redemption',
|
titles: [(text: 'The Shawshank Redemption', language: 'en')],
|
||||||
DateWithPrecisionAndCountry(
|
|
||||||
DateTime(1994, 9, 22), DatePrecision.day, 'US'),
|
|
||||||
)..setDetails(
|
|
||||||
titles: [(title: 'The Shawshank Redemption', language: 'en')],
|
|
||||||
releaseDates: [
|
releaseDates: [
|
||||||
DateWithPrecisionAndCountry(
|
DateWithPrecisionAndCountry(
|
||||||
DateTime(1994, 9, 22), DatePrecision.day, 'US')
|
DateTime(1994, 9, 22), DatePrecision.day, 'US')
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: MoviePage(movie),
|
body: MoviePage(movie),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.text('en'), findsOneWidget);
|
expect(find.text('en'), findsOneWidget);
|
||||||
expect(find.text('The Shawshank Redemption'), findsNWidgets(2));
|
expect(find.text('The Shawshank Redemption'), findsNWidgets(2));
|
||||||
|
|
||||||
expect(find.text('US'), findsOneWidget);
|
expect(find.text('US'), findsOneWidget);
|
||||||
expect(find.textContaining('1994'), findsOneWidget);
|
expect(find.textContaining('1994'), findsOneWidget);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue