diff --git a/lib/application/charts/charts_bloc.dart b/lib/application/charts/charts_bloc.dart index 357e55375..bd3a04bb6 100644 --- a/lib/application/charts/charts_bloc.dart +++ b/lib/application/charts/charts_bloc.dart @@ -25,6 +25,13 @@ class ChartsBloc extends Bloc { yield s; } + //TODO: MAYBE REMOVE THIS FUNCTION FROM HERE + //Some versions were skipped (e.g: 1.7, 1.8, 1.9), that's why we use this function + //to determine if the version can be skipped or no + static bool isValidVersion(double value) { + return value + 1 < 1.7 || value + 1 >= 2; + } + Future _init() async { await _telemetryService.trackChartsOpened(); final tops = [ diff --git a/lib/infrastructure/genshin_service.dart b/lib/infrastructure/genshin_service.dart index b8d098c6e..54f164a44 100644 --- a/lib/infrastructure/genshin_service.dart +++ b/lib/infrastructure/genshin_service.dart @@ -1017,9 +1017,20 @@ class GenshinServiceImpl implements GenshinService { ..sort((x, y) => x.version.compareTo(y.version)); final charts = []; final characters = getCharactersForCard(); + final usedChars = >{}; for (final banner in banners) { for (final key in banner.itemKeys) { + final bannerHasAlreadyBeenAdded = usedChars.containsKey(banner.version); + final characterAlreadyAppearedInThisBanner = usedChars.entries.any((el) => el.key == banner.version && el.value.contains(key)); + if (!bannerHasAlreadyBeenAdded) { + usedChars.putIfAbsent(banner.version, () => [key]); + } else if (characterAlreadyAppearedInThisBanner) { + continue; + } else { + usedChars.update(banner.version, (value) => [...value, key]); + } + final char = characters.firstWhere((el) => el.key == key); final existing = charts.firstWhereOrNull((el) => el.type == char.elementType); final points = existing?.points ?? []; diff --git a/lib/presentation/banner_history/widgets/version_details_dialog.dart b/lib/presentation/banner_history/widgets/version_details_dialog.dart index 3c58b9f91..ce2e2810e 100644 --- a/lib/presentation/banner_history/widgets/version_details_dialog.dart +++ b/lib/presentation/banner_history/widgets/version_details_dialog.dart @@ -17,11 +17,16 @@ const _dateFormat = 'yyyy/MM/dd'; class VersionDetailsDialog extends StatelessWidget { final double version; + final bool showWeapons; + final bool showCharacters; const VersionDetailsDialog({ Key? key, required this.version, - }) : super(key: key); + this.showCharacters = true, + this.showWeapons = true, + }) : assert(!(showCharacters == false && showWeapons == false), 'You must show either characters, weapons or both'), + super(key: key); @override Widget build(BuildContext context) { @@ -48,6 +53,8 @@ class VersionDetailsDialog extends StatelessWidget { return _VersionDetailPeriod( from: group.from, until: group.until, + showCharacters: showCharacters, + showWeapons: showWeapons, items: e.expand((el) => el.items).toList(), ); }, @@ -73,12 +80,16 @@ class _VersionDetailPeriod extends StatelessWidget { final DateTime from; final DateTime until; final List items; + final bool showWeapons; + final bool showCharacters; const _VersionDetailPeriod({ Key? key, required this.from, required this.until, required this.items, + required this.showCharacters, + required this.showWeapons, }) : super(key: key); @override @@ -90,6 +101,10 @@ class _VersionDetailPeriod extends StatelessWidget { final characters = items.where((el) => el.type == ItemType.character).toList()..sort((x, y) => y.rarity.compareTo(x.rarity)); final weapons = items.where((el) => el.type == ItemType.weapon).toList()..sort((x, y) => y.rarity.compareTo(x.rarity)); + if (characters.isEmpty && !showWeapons || weapons.isEmpty && !showCharacters) { + return const SizedBox.shrink(); + } + return Container( margin: const EdgeInsets.only(bottom: 20), child: Column( @@ -103,14 +118,14 @@ class _VersionDetailPeriod extends StatelessWidget { ], ), Divider(color: theme.colorScheme.primary), - if (characters.isNotEmpty) Text(s.characters, style: theme.textTheme.subtitle1), - if (characters.isNotEmpty) + if (characters.isNotEmpty && showCharacters) Text(s.characters, style: theme.textTheme.subtitle1), + if (characters.isNotEmpty && showCharacters) _Items( type: BannerHistoryItemType.character, items: characters, ), - if (weapons.isNotEmpty) Text(s.weapons, style: theme.textTheme.subtitle1), - if (weapons.isNotEmpty) + if (weapons.isNotEmpty && showWeapons) Text(s.weapons, style: theme.textTheme.subtitle1), + if (weapons.isNotEmpty && showWeapons) _Items( type: BannerHistoryItemType.weapon, items: weapons, diff --git a/lib/presentation/charts/charts_page.dart b/lib/presentation/charts/charts_page.dart index 55c397e4b..67b49d054 100644 --- a/lib/presentation/charts/charts_page.dart +++ b/lib/presentation/charts/charts_page.dart @@ -6,6 +6,7 @@ import 'package:shiori/domain/enums/enums.dart'; import 'package:shiori/domain/extensions/iterable_extensions.dart'; import 'package:shiori/generated/l10n.dart'; import 'package:shiori/injection.dart'; +import 'package:shiori/presentation/banner_history/widgets/version_details_dialog.dart'; import 'package:shiori/presentation/character/character_page.dart'; import 'package:shiori/presentation/charts/widgets/chart_card.dart'; import 'package:shiori/presentation/charts/widgets/chart_legend.dart'; @@ -182,7 +183,17 @@ class ChartsPage extends StatelessWidget { ) .toList(), ), - child: HorizontalBarChart(items: state.filteredElements), + child: HorizontalBarChart( + items: state.filteredElements, + canValueBeRendered: ChartsBloc.isValidVersion, + onPointTap: (version) => showDialog( + context: context, + builder: (_) => VersionDetailsDialog( + version: version, + showWeapons: false, + ), + ), + ), ), Text(s.birthdays, style: theme.textTheme.headline5), ChartCard( diff --git a/lib/presentation/charts/widgets/horizontal_bar_chart.dart b/lib/presentation/charts/widgets/horizontal_bar_chart.dart index de2c9a3ab..b3357fae1 100644 --- a/lib/presentation/charts/widgets/horizontal_bar_chart.dart +++ b/lib/presentation/charts/widgets/horizontal_bar_chart.dart @@ -6,12 +6,19 @@ import 'package:shiori/domain/models/models.dart'; import 'package:shiori/presentation/shared/extensions/element_type_extensions.dart'; import 'package:shiori/presentation/shared/styles.dart'; +typedef CanValueBeRendered = bool Function(double); +typedef OnPointTap = void Function(double); + class HorizontalBarChart extends StatelessWidget { final List items; + final CanValueBeRendered canValueBeRendered; + final OnPointTap? onPointTap; const HorizontalBarChart({ Key? key, required this.items, + required this.canValueBeRendered, + this.onPointTap, }) : super(key: key); @override @@ -33,6 +40,12 @@ class HorizontalBarChart extends StatelessWidget { maxY: maxY, lineTouchData: LineTouchData( handleBuiltInTouches: true, + touchCallback: (event, response) { + if (event is FlTapUpEvent && response?.lineBarSpots != null && response!.lineBarSpots!.isNotEmpty) { + final version = response.lineBarSpots!.first.x + 1; + onPointTap?.call(version); + } + }, touchTooltipData: LineTouchTooltipData( tooltipBgColor: theme.backgroundColor, ), @@ -45,7 +58,7 @@ class HorizontalBarChart extends StatelessWidget { reservedSize: 32, interval: xIntervals, //TODO: TOOLTIPS - getTitlesWidget: (value, meta) => value + 1 >= 1.7 && value + 1 < 2 + getTitlesWidget: (value, meta) => !canValueBeRendered(value) ? Container() : Padding( padding: const EdgeInsets.only(top: 10.0),