release_schedule/lib/model/movie_manager.dart

154 lines
4.1 KiB
Dart
Raw Normal View History

2023-11-08 14:43:59 +01:00
import 'dart:async';
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/delayed_function_caller.dart';
2023-11-08 14:43:59 +01:00
import 'package:release_schedule/model/local_movie_storage.dart';
import 'package:release_schedule/model/movie.dart';
import 'package:release_schedule/model/search.dart';
2023-11-08 14:43:59 +01:00
final movieManager = MovieManager(WikidataMovieApi(),
LocalMovieStorageGetStorage(WikidataMovieData.fromEncodable));
2023-11-08 14:43:59 +01:00
class MovieManager extends ChangeNotifier {
final List<MovieData> movies = List.empty(growable: true);
final LocalMovieStorage cache;
final MovieApi api;
bool loading = false;
late final DelayedFunctionCaller cacheUpdater;
2023-11-08 14:43:59 +01:00
bool cacheLoaded = false;
2023-11-08 14:43:59 +01:00
MovieManager(this.api, this.cache) {
cacheUpdater = DelayedFunctionCaller(() {
cache.update(movies);
}, const Duration(seconds: 3));
_loadCache();
}
2023-11-11 15:05:11 +01:00
Future<void> _loadCache() async {
2023-11-08 14:43:59 +01:00
addMovies(await cache.retrieve());
}
2023-11-11 15:05:11 +01:00
void _moviesModified({bool withoutAddingOrRemoving = false}) {
cacheUpdater.call();
2023-11-08 14:43:59 +01:00
if (!withoutAddingOrRemoving) {
// only notify listeners if movies are added or removed
// if they are modified in place they will notify listeners themselves
notifyListeners();
}
}
List<MovieData> addMovies(List<MovieData> additionalMovies) {
List<MovieData> actualMovies = [];
bool added = false;
for (var movie in additionalMovies) {
2023-11-08 14:43:59 +01:00
MovieData? existing =
firstWhereOrNull(movies, (element) => movie.same(element));
if (existing == null) {
2023-11-08 14:59:23 +01:00
_insertMovie(movie);
2023-11-08 14:43:59 +01:00
movie.addListener(() {
_moviesModified(withoutAddingOrRemoving: true);
2023-11-11 15:08:08 +01:00
_resortMovies();
2023-11-08 14:43:59 +01:00
});
added = true;
actualMovies.add(movie);
} else {
2024-01-08 12:57:36 +01:00
existing.updateWithNewIgnoringUserControlled(movie);
actualMovies.add(existing);
}
}
if (added) {
2023-11-08 14:43:59 +01:00
_moviesModified();
}
return actualMovies;
}
2023-11-11 15:05:11 +01:00
void _insertMovie(MovieData movie) {
2023-11-08 14:59:23 +01:00
int min = 0;
int max = movies.length - 1;
2024-01-08 11:56:35 +01:00
while (min <= max) {
int center = (min + max) ~/ 2;
2023-11-16 12:51:45 +01:00
int diff =
movie.releaseDate.date.compareTo(movies[center].releaseDate.date);
2023-11-08 14:59:23 +01:00
if (diff < 0) {
max = center - 1;
} else {
min = center + 1;
}
}
movies.insert(min, movie);
}
2023-11-11 15:08:08 +01:00
void _resortMovies() {
for (int i = 0; i < movies.length; i++) {
var temp = movies[i];
int j = i - 1;
2023-11-16 12:51:45 +01:00
for (;
j >= 0 && movies[j].releaseDate.date.isAfter(temp.releaseDate.date);
j--) {
2023-11-11 15:08:08 +01:00
movies[j + 1] = movies[j];
}
movies[j + 1] = temp;
}
}
2023-11-11 15:05:11 +01:00
void removeMoviesWhere(bool Function(MovieData movie) test) {
2023-11-08 14:43:59 +01:00
bool removedMovies = false;
for (int i = movies.length - 1; i >= 0; i--) {
bool remove = test(movies[i]);
if (remove) {
removedMovies = true;
movies.removeAt(i);
}
}
if (removedMovies) {
_moviesModified();
}
}
/// Only search locally cached movies.
List<MovieData> localSearch(String search) {
var results = searchList(
movies,
search,
(movie) => [
movie.title,
...(movie.titles?.map((title) => title.title) ?? []),
]);
return results;
}
/// Online search for movies.
Future<List<MovieData>> onlineSearch(String search) async {
2023-11-08 14:43:59 +01:00
List<MovieData> movies = await api.searchForMovies(search);
return addMovies(movies);
}
2023-11-11 15:05:11 +01:00
void expandDetails(List<MovieData> movies) {
api.addMovieDetails(movies);
}
2023-11-11 15:05:11 +01:00
Future<void> loadUpcomingMovies() async {
2023-11-08 14:43:59 +01:00
try {
loading = true;
notifyListeners();
List<MovieData> movies = await api
.getUpcomingMovies(DateTime.now().subtract(const Duration(days: 7)));
2023-11-08 14:43:59 +01:00
addMovies(movies);
} finally {
loading = false;
notifyListeners();
}
}
}
T? firstWhereOrNull<T>(List<T> list, bool Function(T element) test) {
try {
return list.firstWhere(test);
} catch (e) {
return null;
}
}