2023-11-08 14:43:59 +01:00
|
|
|
import 'dart:async';
|
|
|
|
|
2023-11-06 10:38:26 +01:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:release_schedule/api/movie_api.dart';
|
|
|
|
import 'package:release_schedule/api/wikidata_movie_api.dart';
|
2023-11-16 13:12:55 +01:00
|
|
|
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';
|
2023-11-06 10:38:26 +01:00
|
|
|
import 'package:release_schedule/model/movie.dart';
|
|
|
|
|
2023-11-08 14:43:59 +01:00
|
|
|
final movieManager = MovieManager(WikidataMovieApi(),
|
|
|
|
LocalMovieStorageGetStorage(WikidataMovieData.fromEncodable));
|
2023-11-06 10:38:26 +01:00
|
|
|
|
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;
|
|
|
|
DelayedFunctionCaller? cacheUpdater;
|
|
|
|
bool cacheLoaded = false;
|
2023-11-06 10:38:26 +01:00
|
|
|
|
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}) {
|
2023-11-08 14:43:59 +01:00
|
|
|
cacheUpdater?.call();
|
|
|
|
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 = [];
|
2023-11-06 10:38:26 +01:00
|
|
|
bool added = false;
|
|
|
|
for (var movie in additionalMovies) {
|
2023-11-08 14:43:59 +01:00
|
|
|
MovieData? existing =
|
2023-11-06 10:38:26 +01:00
|
|
|
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
|
|
|
});
|
2023-11-06 10:38:26 +01:00
|
|
|
added = true;
|
|
|
|
actualMovies.add(movie);
|
|
|
|
} else {
|
2023-11-08 14:59:23 +01:00
|
|
|
existing.updateWithNew(movie);
|
2023-11-06 10:38:26 +01:00
|
|
|
actualMovies.add(existing);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (added) {
|
2023-11-08 14:43:59 +01:00
|
|
|
_moviesModified();
|
2023-11-06 10:38:26 +01:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
while (min - 1 < max) {
|
|
|
|
int center = ((min + max) / 2).floor();
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-06 10:38:26 +01:00
|
|
|
/// Only search locally cached movies.
|
|
|
|
localSearch(String search) {}
|
|
|
|
|
|
|
|
/// Online search for movies.
|
2023-11-08 14:43:59 +01:00
|
|
|
Future<List<MovieData>> search(String search) async {
|
|
|
|
List<MovieData> movies = await api.searchForMovies(search);
|
2023-11-06 10:38:26 +01:00
|
|
|
return addMovies(movies);
|
|
|
|
}
|
|
|
|
|
2023-11-11 15:05:11 +01:00
|
|
|
void expandDetails(List<MovieData> movies) {
|
2023-11-06 10:38:26 +01:00
|
|
|
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();
|
2023-11-11 14:50:20 +01:00
|
|
|
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();
|
|
|
|
}
|
2023-11-06 10:38:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-16 13:12:55 +01:00
|
|
|
T? firstWhereOrNull<T>(List<T> list, bool Function(T element) test) {
|
|
|
|
try {
|
|
|
|
return list.firstWhere(test);
|
|
|
|
} catch (e) {
|
|
|
|
return null;
|
2023-11-06 10:38:26 +01:00
|
|
|
}
|
|
|
|
}
|