import 'package:flutter/material.dart'; import 'package:release_schedule/model/dates.dart'; class MovieData extends ChangeNotifier { bool _bookmarked = false; String? _title; DateWithPrecisionAndCountry? _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?>? _titles; Dated?>? _labels; Dated?>? _releaseDates; Dated? _description; Dated?>? _genres; MovieData(); String? get title { return _title; } DateWithPrecisionAndCountry? get releaseDate { return _releaseDate; } bool get bookmarked { return _bookmarked; } Dated? get description { return _description; } Dated?>? get releaseDates { return _releaseDates; } Dated?>? get genres { return _genres; } Dated?>? get titles { return _titles; } Dated?>? get labels { return _labels; } /// 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. void updateWithNewIgnoringUserControlled(MovieData movie) { setDetails( titles: movie.titles, labels: movie.labels, releaseDates: movie.releaseDates, genres: movie.genres, description: movie.description, ); } void setNewDetails({ bool? bookmarked, List? titles, List? labels, List? releaseDates, List? genres, String? description, }) { setDetails( bookmarked: bookmarked, titles: titles != null ? Dated.now(titles) : null, 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?>? titles, Dated?>? labels, Dated?>? releaseDates, Dated?>? genres, Dated? description, }) { if (bookmarked != null) { _bookmarked = bookmarked; } if (titles != null) { _titles = titles; } 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(); } @override String toString() { return "$title (${_releaseDate.toString()}${_genres?.value?.isNotEmpty ?? false ? "; ${_genres?.value?.join(", ")}" : ""})"; } bool same(MovieData other) { return title != null && title == other.title && (releaseDate == null || other.releaseDate == null || releaseDate!.dateWithPrecision.date.year == other.releaseDate!.dateWithPrecision.date.year); } Map toJsonEncodable() { dynamic releaseDatesByCountry = _releaseDates?.toJsonEncodable( (releaseDates) => releaseDates ?.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 { "bookmarked": _bookmarked, "titles": titlesByCountry, "labels": labels, "releaseDates": releaseDatesByCountry, "genres": genres, "description": _description?.toJsonEncodable((description) => description), }; } MovieData.fromJsonEncodable(Map json) { setDetails( bookmarked: json["bookmarked"] as bool? ?? false, titles: decodeOptionalJson?>>( json["titles"], (json) => Dated.fromJsonEncodable( json, (value) => (value as List) .map((title) => (text: title[0], language: title[1]) as TextInLanguage) .toList())), labels: decodeOptionalJson?>>( json["labels"], (json) => Dated.fromJsonEncodable( json, (value) => (value as List) .map((label) => (text: label[0], language: label[1]) as TextInLanguage) .toList())), genres: decodeOptionalJson?>>( json["genres"], (json) => Dated.fromJsonEncodable(json, (value) => value.cast())), releaseDates: decodeOptionalJson?>>( json["releaseDates"], (json) => Dated.fromJsonEncodable( json, (value) => (value as List) .map((releaseDate) => DateWithPrecisionAndCountry.fromJsonEncodable( releaseDate)) .toList())), description: decodeOptionalJson>(json["description"], (json) => Dated.fromJsonEncodable(json, (value) => value)), ); } } T? decodeOptionalJson(dynamic json, T Function(dynamic) decode) { if (json == null) { return null; } return decode(json); } typedef TextInLanguage = ({String text, String language}); class DateWithPrecisionAndCountry { final DateWithPrecision dateWithPrecision; final String country; DateWithPrecisionAndCountry( DateTime date, DatePrecision precision, this.country) : dateWithPrecision = DateWithPrecision(date, precision); DateWithPrecisionAndCountry.fromJsonEncodable(List json) : dateWithPrecision = DateWithPrecision.fromJsonEncodable(json), country = json[2]; toJsonEncodable() { return dateWithPrecision.toJsonEncodable() + [country]; } @override String toString() { return "${dateWithPrecision.toString()} ($country)"; } }