2023-10-20 14:12:43 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2024-01-15 19:47:10 +00:00
|
|
|
import 'package:lightmeter/utils/context_utils.dart';
|
2023-10-20 14:12:43 +00:00
|
|
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
|
|
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|
|
|
|
|
|
|
class FilmsProvider extends StatefulWidget {
|
|
|
|
final IAPStorageService storageService;
|
|
|
|
final List<Film>? availableFilms;
|
|
|
|
final Widget child;
|
|
|
|
|
|
|
|
const FilmsProvider({
|
|
|
|
required this.storageService,
|
|
|
|
this.availableFilms,
|
|
|
|
required this.child,
|
|
|
|
super.key,
|
|
|
|
});
|
|
|
|
|
|
|
|
static FilmsProviderState of(BuildContext context) {
|
|
|
|
return context.findAncestorStateOfType<FilmsProviderState>()!;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<FilmsProvider> createState() => FilmsProviderState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class FilmsProviderState extends State<FilmsProvider> {
|
2024-10-25 11:01:37 +00:00
|
|
|
late final Map<String, _SelectableFilm<Film>> predefinedFilms = Map.fromEntries(
|
|
|
|
(widget.availableFilms ?? films).map(
|
|
|
|
(film) => MapEntry(
|
|
|
|
film.id,
|
|
|
|
(
|
|
|
|
film: film,
|
|
|
|
selected: widget.storageService.filmsInUse.contains(film),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
final Map<String, _SelectableFilm<FilmExponential>> customFilms = {};
|
2023-10-20 14:12:43 +00:00
|
|
|
late Film _selected;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
_selected = widget.storageService.selectedFilm;
|
|
|
|
_discardSelectedIfNotIncluded();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Films(
|
2024-10-25 11:01:37 +00:00
|
|
|
predefinedFilms: predefinedFilms,
|
|
|
|
customFilms: customFilms,
|
2024-10-06 18:26:40 +00:00
|
|
|
selected: context.isPro ? _selected : const FilmStub(),
|
2023-10-20 14:12:43 +00:00
|
|
|
child: widget.child,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-10-25 11:01:37 +00:00
|
|
|
/* Both type of films **/
|
|
|
|
|
|
|
|
void toggleFilm(Film film, bool enabled) {
|
|
|
|
Film? targetFilm = predefinedFilms[film.id]?.film;
|
|
|
|
if (targetFilm != null) {
|
|
|
|
predefinedFilms[film.id] = (film: film, selected: enabled);
|
|
|
|
_discardSelectedIfNotIncluded();
|
|
|
|
setState(() {});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
targetFilm = customFilms[film.id]?.film;
|
|
|
|
if (targetFilm != null) {
|
|
|
|
customFilms[film.id] = (film: film as FilmExponential, selected: enabled);
|
|
|
|
_discardSelectedIfNotIncluded();
|
|
|
|
setState(() {});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void selectFilm(Film film) {
|
2023-10-20 14:12:43 +00:00
|
|
|
if (_selected != film) {
|
|
|
|
_selected = film;
|
|
|
|
widget.storageService.selectedFilm = film;
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-25 11:01:37 +00:00
|
|
|
/* Custom films **/
|
|
|
|
|
|
|
|
void addCustomFilm(FilmExponential film) {
|
|
|
|
customFilms[film.id] = (film: film, selected: false);
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateCustomFilm(FilmExponential film) {
|
|
|
|
customFilms[film.id] = (film: film, selected: customFilms[film.id]!.selected);
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: add delete button to UI
|
|
|
|
void deleteCustomFilm(FilmExponential film) {
|
|
|
|
customFilms.remove(film.id);
|
2023-10-20 14:12:43 +00:00
|
|
|
_discardSelectedIfNotIncluded();
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
|
|
|
|
void _discardSelectedIfNotIncluded() {
|
2024-10-25 11:01:37 +00:00
|
|
|
if (_selected != const FilmStub() &&
|
|
|
|
!predefinedFilms.values.any((e) => e.film == _selected) &&
|
|
|
|
!customFilms.values.any((e) => e.film == _selected)) {
|
2024-10-06 18:26:40 +00:00
|
|
|
_selected = const FilmStub();
|
|
|
|
widget.storageService.selectedFilm = const FilmStub();
|
2023-10-20 14:12:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-25 11:01:37 +00:00
|
|
|
typedef _SelectableFilm<T extends Film> = ({T film, bool selected});
|
|
|
|
|
|
|
|
enum _FilmsModelAspect {
|
|
|
|
customFilmsList,
|
|
|
|
predefinedFilmsList,
|
|
|
|
filmsInUse,
|
|
|
|
selected,
|
|
|
|
}
|
|
|
|
|
|
|
|
class Films extends InheritedModel<_FilmsModelAspect> {
|
|
|
|
final Map<String, _SelectableFilm<Film>> predefinedFilms;
|
|
|
|
|
|
|
|
@protected
|
|
|
|
final Map<String, _SelectableFilm<FilmExponential>> customFilms;
|
|
|
|
final Film selected;
|
2023-10-20 14:12:43 +00:00
|
|
|
|
|
|
|
const Films({
|
2024-10-25 11:01:37 +00:00
|
|
|
required this.predefinedFilms,
|
|
|
|
required this.customFilms,
|
|
|
|
required this.selected,
|
2023-10-20 14:12:43 +00:00
|
|
|
required super.child,
|
|
|
|
});
|
|
|
|
|
2024-10-25 11:01:37 +00:00
|
|
|
static List<Film> predefinedFilmsOf<T>(BuildContext context) {
|
|
|
|
return InheritedModel.inheritFrom<Films>(context, aspect: _FilmsModelAspect.predefinedFilmsList)!
|
|
|
|
.predefinedFilms
|
|
|
|
.values
|
|
|
|
.map((value) => value.film)
|
|
|
|
.toList();
|
|
|
|
}
|
|
|
|
|
|
|
|
static List<FilmExponential> customFilmsOf<T>(BuildContext context) {
|
|
|
|
return InheritedModel.inheritFrom<Films>(context, aspect: _FilmsModelAspect.customFilmsList)!
|
|
|
|
.customFilms
|
|
|
|
.values
|
|
|
|
.map((value) => value.film)
|
|
|
|
.toList();
|
2023-10-20 14:12:43 +00:00
|
|
|
}
|
|
|
|
|
2024-10-06 18:26:40 +00:00
|
|
|
/// [FilmStub()] + films in use selected by user
|
2023-10-20 14:12:43 +00:00
|
|
|
static List<Film> inUseOf<T>(BuildContext context) {
|
2024-10-25 11:01:37 +00:00
|
|
|
final model = InheritedModel.inheritFrom<Films>(context, aspect: _FilmsModelAspect.filmsInUse)!;
|
|
|
|
return [
|
|
|
|
const FilmStub(),
|
|
|
|
...model.customFilms.values.where((e) => e.selected).map((e) => e.film),
|
|
|
|
...model.predefinedFilms.values.where((e) => e.selected).map((e) => e.film),
|
|
|
|
];
|
2023-10-20 14:12:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static Film selectedOf(BuildContext context) {
|
2024-10-25 11:01:37 +00:00
|
|
|
return InheritedModel.inheritFrom<Films>(context, aspect: _FilmsModelAspect.selected)!.selected;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool updateShouldNotify(Films _) => true;
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool updateShouldNotifyDependent(Films oldWidget, Set<_FilmsModelAspect> dependencies) {
|
|
|
|
if (dependencies.contains(_FilmsModelAspect.customFilmsList)) {}
|
|
|
|
if (dependencies.contains(_FilmsModelAspect.selected)) {
|
|
|
|
return selected != oldWidget.selected;
|
|
|
|
} else {
|
|
|
|
// TODO: reduce unnecessary notifications
|
|
|
|
return true;
|
|
|
|
}
|
2023-10-20 14:12:43 +00:00
|
|
|
}
|
|
|
|
}
|