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);
|
|
|
|
|
}
|
|
|
|
|
}
|