Skip to content

Commit

Permalink
[Presentation] Added initial banner history period detail
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolfteam committed Apr 16, 2022
1 parent f202879 commit 3655f39
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 15 deletions.
3 changes: 0 additions & 3 deletions lib/application/banner_history/banner_history_bloc.dart
Expand Up @@ -24,9 +24,6 @@ class BannerHistoryBloc extends Bloc<BannerHistoryEvent, BannerHistoryState> {
final List<BannerHistoryItemModel> _characterBanners = [];
final List<BannerHistoryItemModel> _weaponBanners = [];

//TODO: ON DESC ORDER YOU MAY WANT TO CHANGE THE WAY YOU SHOW THE NUMBERS
// SO INSTEAD OF 9 8 7 X... YOU SHOW 1 2 3 X...

BannerHistoryBloc(this._genshinService, this._telemetryService) : super(_initialState);

@override
Expand Down
9 changes: 8 additions & 1 deletion lib/injection.dart
Expand Up @@ -146,7 +146,14 @@ class Injection {

static BannerHistoryBloc get bannerHistoryBloc {
final genshinService = getIt<GenshinService>();
return BannerHistoryBloc(genshinService);
final telemetryService = getIt<TelemetryService>();
return BannerHistoryBloc(genshinService, telemetryService);
}

static BannerHistoryItemBloc get bannerHistoryItemBloc {
final genshinService = getIt<GenshinService>();
final telemetryService = getIt<TelemetryService>();
return BannerHistoryItemBloc(genshinService, telemetryService);
}

//TODO: USE THIS PROP
Expand Down
9 changes: 8 additions & 1 deletion lib/l10n/intl_en.arb
Expand Up @@ -417,6 +417,13 @@
"useDarkAmoledTheme": "Use dark amoled theme",
"unlockedWithDonation": "Unlocked with a donation",
"bannerHistory": "Banner History",
"bannerType": "Banner Type",
"versions": "Versions",
"checkBannerHistory": "Check when a character / weapon was released"
"checkBannerHistory": "Check when a character / weapon was released",
"fromDate": "From: {date}",
"untilDate": "Until: {date}",
"nameAsc": "Name asc.",
"nameDesc": "Name desc.",
"versionAsc": "Version asc.",
"versionDesc": "Version desc."
}
43 changes: 41 additions & 2 deletions lib/presentation/banner_history/banner_history_page.dart
@@ -1,12 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shiori/application/bloc.dart';
import 'package:shiori/domain/enums/enums.dart';
import 'package:shiori/generated/l10n.dart';
import 'package:shiori/injection.dart';
import 'package:shiori/presentation/banner_history/widgets/content.dart';
import 'package:shiori/presentation/banner_history/widgets/fixed_header_row.dart';
import 'package:shiori/presentation/banner_history/widgets/fixed_left_column.dart';
import 'package:shiori/presentation/shared/app_fab.dart';
import 'package:shiori/presentation/shared/extensions/i18n_extensions.dart';
import 'package:shiori/presentation/shared/item_popupmenu_filter.dart';
import 'package:shiori/presentation/shared/mixins/app_fab_mixin.dart';
import 'package:shiori/presentation/shared/sync_controller.dart';

Expand Down Expand Up @@ -37,12 +40,11 @@ class _BannerHistoryPageState extends State<BannerHistoryPage> with SingleTicker

@override
Widget build(BuildContext context) {
final s = S.of(context);
const margin = EdgeInsets.all(4.0);
return BlocProvider(
create: (_) => Injection.bannerHistoryBloc..add(const BannerHistoryEvent.init()),
child: Scaffold(
appBar: AppBar(title: Text(s.bannerHistory)),
appBar: const _AppBar(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expand All @@ -56,7 +58,9 @@ class _BannerHistoryPageState extends State<BannerHistoryPage> with SingleTicker
controller: _fixedHeaderScrollController,
child: BlocBuilder<BannerHistoryBloc, BannerHistoryState>(
builder: (ctx, state) => FixedHeaderRow(
type: state.type,
versions: state.versions,
selectedVersions: state.selectedVersions,
margin: margin,
firstCellWidth: _firstCellWidth,
firstCellHeight: _firstCellHeight,
Expand Down Expand Up @@ -116,3 +120,38 @@ class _BannerHistoryPageState extends State<BannerHistoryPage> with SingleTicker
);
}
}

class _AppBar extends StatelessWidget implements PreferredSizeWidget {
const _AppBar({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<BannerHistoryBloc, BannerHistoryState>(
builder: (ctx, state) => AppBar(
title: Text(s.bannerHistory),
actions: [
ItemPopupMenuFilter<BannerHistoryItemType>(
tooltipText: s.bannerType,
selectedValue: state.type,
values: BannerHistoryItemType.values,
onSelected: (val) => context.read<BannerHistoryBloc>().add(BannerHistoryEvent.typeChanged(type: val)),
icon: const Icon(Icons.swap_horiz),
itemText: (val, _) => s.translateBannerHistoryItemType(val),
),
ItemPopupMenuFilter<BannerHistorySortType>(
tooltipText: s.sortType,
selectedValue: state.sortType,
values: BannerHistorySortType.values,
onSelected: (val) => context.read<BannerHistoryBloc>().add(BannerHistoryEvent.sortTypeChanged(type: val)),
icon: const Icon(Icons.sort),
itemText: (val, _) => s.translateBannerHistorySortType(val),
),
],
),
);
}

@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
193 changes: 187 additions & 6 deletions lib/presentation/banner_history/widgets/fixed_header_row.dart
@@ -1,10 +1,26 @@
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:intl/intl.dart';
import 'package:shiori/application/bloc.dart';
import 'package:shiori/domain/enums/enums.dart';
import 'package:shiori/domain/models/models.dart';
import 'package:shiori/generated/l10n.dart';
import 'package:shiori/injection.dart';
import 'package:shiori/presentation/shared/extensions/media_query_extensions.dart';
import 'package:shiori/presentation/shared/extensions/rarity_extensions.dart';
import 'package:shiori/presentation/shared/images/circle_character.dart';
import 'package:shiori/presentation/shared/images/circle_weapon.dart';
import 'package:shiori/presentation/shared/loading.dart';

const _dateFormat = 'yyyy/MM/dd';

class FixedHeaderRow extends StatelessWidget {
final BannerHistoryItemType type;
final List<double> versions;
final List<double> selectedVersions;
final EdgeInsets margin;
final double firstCellWidth;
final double firstCellHeight;
Expand All @@ -13,7 +29,9 @@ class FixedHeaderRow extends StatelessWidget {

const FixedHeaderRow({
Key? key,
required this.type,
required this.versions,
required this.selectedVersions,
required this.margin,
required this.firstCellWidth,
required this.firstCellHeight,
Expand All @@ -35,20 +53,28 @@ class FixedHeaderRow extends StatelessWidget {
children: List.generate(
versions.length + 1,
(index) => index == 0
? _VersionsCharactersCell(cellWidth: firstCellWidth, cellHeight: firstCellHeight, margin: margin)
: _VersionCard(cellWidth: cellWidth, cellHeight: cellHeight, margin: margin, version: versions[index - 1]),
? _VersionsCharactersCell(type: type, cellWidth: firstCellWidth, cellHeight: firstCellHeight, margin: margin)
: _VersionCard(
cellWidth: cellWidth,
cellHeight: cellHeight,
margin: margin,
version: versions[index - 1],
isSelected: selectedVersions.contains(versions[index - 1]),
),
),
);
}
}

class _VersionsCharactersCell extends StatelessWidget {
final BannerHistoryItemType type;
final EdgeInsets margin;
final double cellWidth;
final double cellHeight;

const _VersionsCharactersCell({
Key? key,
required this.type,
required this.margin,
required this.cellWidth,
required this.cellHeight,
Expand All @@ -58,6 +84,17 @@ class _VersionsCharactersCell extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final s = S.of(context);
String text = '';
switch (type) {
case BannerHistoryItemType.character:
text = s.characters;
break;
case BannerHistoryItemType.weapon:
text = s.weapons;
break;
default:
throw Exception('Invalid banner history item type');
}
return Container(
width: cellWidth,
height: cellHeight,
Expand All @@ -76,7 +113,7 @@ class _VersionsCharactersCell extends StatelessWidget {
),
Divider(color: theme.colorScheme.primary, thickness: 3, indent: 5, endIndent: 5),
Text(
s.characters,
text,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.subtitle2!.copyWith(fontWeight: FontWeight.bold),
Expand All @@ -90,13 +127,15 @@ class _VersionsCharactersCell extends StatelessWidget {

class _VersionCard extends StatelessWidget {
final double version;
final bool isSelected;
final EdgeInsets margin;
final double cellWidth;
final double cellHeight;

const _VersionCard({
Key? key,
required this.version,
required this.isSelected,
required this.margin,
required this.cellWidth,
required this.cellHeight,
Expand All @@ -106,11 +145,12 @@ class _VersionCard extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
return InkWell(
onTap: () {},
onTap: () => context.read<BannerHistoryBloc>().add(BannerHistoryEvent.versionSelected(version: version)),
onLongPress: () => showDialog(context: context, builder: (_) => _VersionDetailsDialog(version: version)),
child: Card(
margin: margin,
color: theme.colorScheme.primary,
elevation: 10,
color: isSelected ? theme.colorScheme.primary.withOpacity(0.45) : theme.colorScheme.primary,
elevation: isSelected ? 0 : 10,
child: Container(
alignment: Alignment.center,
width: cellWidth,
Expand All @@ -126,3 +166,144 @@ class _VersionCard extends StatelessWidget {
);
}
}

class _VersionDetailsDialog extends StatelessWidget {
final double version;

const _VersionDetailsDialog({
Key? key,
required this.version,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final mq = MediaQuery.of(context);
final s = S.of(context);
return BlocProvider<BannerHistoryItemBloc>(
create: (context) => Injection.bannerHistoryItemBloc..add(BannerHistoryItemEvent.init(version: version)),
child: AlertDialog(
title: Text(s.appVersion(version)),
content: SizedBox(
width: mq.getWidthForDialogs(),
child: SingleChildScrollView(
child: BlocBuilder<BannerHistoryItemBloc, BannerHistoryItemState>(
builder: (context, state) => state.maybeMap(
loadedState: (state) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: state.items
.groupListsBy((el) => '${DateFormat(_dateFormat).format(el.from)}_${DateFormat(_dateFormat).format(el.until)}')
.values
.map(
(e) {
final group = e.first;

return _VersionDetailPeriod(
from: group.from,
until: group.until,
items: e.expand((el) => el.items).toList(),
);
},
).toList(),
),
orElse: () => const Loading(useScaffold: false),
),
),
),
),
actions: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: Text(s.ok),
)
],
),
);
}
}

class _VersionDetailPeriod extends StatelessWidget {
final DateTime from;
final DateTime until;
final List<ItemCommonWithRarityAndType> items;

const _VersionDetailPeriod({
Key? key,
required this.from,
required this.until,
required this.items,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final s = S.of(context);
final theme = Theme.of(context);
final from = DateFormat(_dateFormat).format(this.from);
final until = DateFormat(_dateFormat).format(this.until);
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));

return Container(
margin: const EdgeInsets.only(bottom: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(s.fromDate(from), style: theme.textTheme.subtitle1!.copyWith(fontWeight: FontWeight.bold)),
Text(s.untilDate(until), style: theme.textTheme.subtitle1!.copyWith(fontWeight: FontWeight.bold)),
],
),
Divider(color: theme.colorScheme.primary),
if (characters.isNotEmpty) Text(s.characters, style: theme.textTheme.subtitle1),
if (characters.isNotEmpty)
_Items(
type: BannerHistoryItemType.character,
items: characters,
),
if (weapons.isNotEmpty) Text(s.weapons, style: theme.textTheme.subtitle1),
if (weapons.isNotEmpty)
_Items(
type: BannerHistoryItemType.weapon,
items: weapons,
),
],
),
);
}
}

class _Items extends StatelessWidget {
final BannerHistoryItemType type;
final List<ItemCommonWithRarityAndType> items;

const _Items({
Key? key,
required this.type,
required this.items,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return SizedBox(
height: type == BannerHistoryItemType.character ? 80 : 70,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
itemCount: items.length,
itemBuilder: (ctx, index) {
final item = items[index];
final gradient = item.rarity.getRarityGradient();
switch (type) {
case BannerHistoryItemType.character:
return CircleCharacter(itemKey: item.key, image: item.image, gradient: gradient);
case BannerHistoryItemType.weapon:
return CircleWeapon(itemKey: item.key, image: item.image, gradient: gradient);
default:
throw Exception('Banner history item type = $type is not valid');
}
},
),
);
}
}

0 comments on commit 3655f39

Please sign in to comment.