mnemo_cards/lib/widgets/card_pack/cards_view_controller.dart

200 lines
5.1 KiB
Dart
Raw Normal View History

2024-05-28 21:25:51 +00:00
import 'dart:async';
import 'dart:math';
import 'package:flutter/widgets.dart';
import 'package:mnemo_cards/features/packs/images_holder.dart';
2024-06-13 05:06:50 +00:00
import 'package:mnemo_cards/main.dart';
import 'package:mnemo_cards/utils/iterable_helper.dart';
2024-05-28 21:25:51 +00:00
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:rxdart/rxdart.dart';
import '../../di/locator.dart';
class CardsViewController extends ChangeNotifier {
bool _expanded = false;
bool _favorite = false;
double imageWidth = 0.0;
List<GameCardDto> cards = [];
2024-06-13 05:06:50 +00:00
List<int> allCardsOrder = [];
List<int> favoriteCardsOrder = [];
List<int> get cardsOrder => _favorite ? favoriteCardsOrder : allCardsOrder;
2024-05-28 21:25:51 +00:00
final CardPackDto cardPack;
final ScrollController _cardsScrollController =
ScrollController(keepScrollOffset: true);
ScrollController get cardsScrollController => _cardsScrollController;
StreamController<CardsViewState> _streamController =
StreamController.broadcast();
Stream<CardsViewState> get asStream =>
_streamController.stream.startWith(state).distinct();
Stream<bool> get expandedStream => asStream.map((s) => s.expanded).distinct();
Stream<bool> get favoriteStream => asStream.map((s) => s.favorite).distinct();
Stream<List<GameCardDto>> get cardsStream =>
asStream.map((s) => s.packCards).distinct((p, n) =>
(p.length == n.length) &&
p.indexed.every((p) => n[p.$1].id == p.$2.id));
CardsViewState get state => CardsViewState(
expanded: _expanded,
favorite: _favorite,
packCards: cards,
);
List<GameCardDto> _filterPackCards() => cardPack.cards
.where(
(card) =>
!_favorite ||
locator.favoriteCardsController.isFavoriteCard(card.id),
)
.toList();
void setExpanded(bool expanded) {
if (cards.isNotEmpty) {
_expanded = expanded;
notifyListeners();
}
}
2024-06-13 05:06:50 +00:00
Future<void> _saveCardsOrder() async {
if (_favorite) {
await globalSharedPreferences.setString(
'sort_fav_${cardPack.id}',
favoriteCardsOrder.encode(),
);
} else {
await globalSharedPreferences.setString(
'sort_${cardPack.id}',
allCardsOrder.encode(),
);
}
}
void _loadCardsOrder() {
favoriteCardsOrder = globalSharedPreferences
.getString(
'sort_fav_${cardPack.id}',
)
?.decodeList<int>() ??
[];
allCardsOrder = globalSharedPreferences
.getString(
'sort_${cardPack.id}',
)
?.decodeList<int>() ??
[];
if (allCardsOrder.isEmpty) {
allCardsOrder = cardPack.cards.map((c) => c.id).toList();
}
if (favoriteCardsOrder.isEmpty) {
favoriteCardsOrder = allCardsOrder.toList();
}
}
void setCardsOrder(List<int> order) {
if (_favorite) {
favoriteCardsOrder = order.toList();
} else {
allCardsOrder = order.toList();
}
_saveCardsOrder();
_updateCards();
}
void onOrderChanged(int from, int to) {
if (_favorite) {
favoriteCardsOrder.move(from, to);
} else {
allCardsOrder.move(from, to);
}
_saveCardsOrder();
_updateCards();
}
2024-05-28 21:25:51 +00:00
void setFavorite(bool favorite) {
_favorite = favorite;
_updateCards();
notifyListeners();
}
void _addToStream() {
_streamController.add(state);
}
double get offset => _cardsScrollController.offset;
int get offsetCards => (_cardsScrollController.offset / imageWidth).floor();
void toggleExpanded() => setExpanded(!_expanded);
CardsViewController(this.cardPack) {
addListener(_addToStream);
2024-06-13 05:06:50 +00:00
_loadCardsOrder();
2024-05-28 21:25:51 +00:00
_updateCards();
}
void _updateCards() {
cards = _filterPackCards();
2024-06-13 05:06:50 +00:00
cards
..sort((p, n) {
final pIndex = cardsOrder.indexOf(p.id);
final nIndex = cardsOrder.indexOf(n.id);
if (pIndex == -1) {
return nIndex == -1 ? p.id.compareTo(n.id) : 1;
} else if (nIndex == -1) {
return -1;
}
return pIndex.compareTo(nIndex);
});
2024-05-28 21:25:51 +00:00
notifyListeners();
}
Future<void> animateTo(int index) {
final millis =
((_cardsScrollController.offset / imageWidth - index).abs() * 30.0)
.ceil();
return _cardsScrollController.animateTo(
imageWidth * index,
duration: Duration(milliseconds: max(millis, 100)),
curve: Curves.easeInOut,
);
}
void jumpTo(int index) {
return _cardsScrollController.jumpTo(
imageWidth * index,
);
}
}
class CardsViewState {
final bool expanded;
final bool favorite;
final List<GameCardDto> packCards;
CardsViewState({
required this.expanded,
required this.favorite,
required this.packCards,
});
@override
int get hashCode => expanded.hashCode ^ expanded.hashCode ^ packCards.length;
@override
bool operator ==(Object other) {
return (other is CardsViewState) &&
expanded == other.expanded &&
favorite == other.favorite &&
(packCards.length == other.packCards.length) &&
packCards.indexed.every((p) => other.packCards[p.$1].id == p.$2.id);
}
}