Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add some basic mastery tracking for arsenal items #570

Open
wants to merge 2 commits into
base: beta
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
- drift: true
2 changes: 1 addition & 1 deletion lib/app/view/app_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class AppView extends StatelessWidget {
child: child,
);
},
child: SafeArea(child: children[currentIndex]),
child: children[currentIndex],
),
bottomNavigationBar: NavigationBar(
onDestinationSelected: (i) {
Expand Down
24 changes: 2 additions & 22 deletions lib/app/view/app_root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,38 +76,18 @@ class _NavisAppState extends State<NavisApp> with WidgetsBindingObserver {
Locale? locale,
Iterable<Locale> supportedLocales,
) {
const defaultLocale = Locale('en');
Locale? newLocale;

var newLocale = const Locale('en');
for (final supportedLocale in supportedLocales) {
if (locale?.languageCode == supportedLocale.languageCode) {
newLocale = supportedLocale;
}
}

newLocale ??= defaultLocale;

final userSettingsCubit = context.read<UserSettingsCubit>();
final settings = userSettingsCubit.state;
final language = switch (settings) {
UserSettingsSuccess() => settings.language,
_ => defaultLocale
};

if (language != newLocale) {
userSettingsCubit.updateLanguage(newLocale);
}
context.read<UserSettingsCubit>().updateLanguage(newLocale);

return newLocale;
}

@override
void didChangeDependencies() {
super.didChangeDependencies();

BlocProvider.of<WorldstateCubit>(context).fetchWorldstate();
}

@override
Widget build(BuildContext context) {
final settings = context.watch<UserSettingsCubit>().state;
Expand Down
30 changes: 25 additions & 5 deletions lib/app/widgets/bloc_bootstrap.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:navis/arsenal/cubit/arsenal_cubit.dart';
import 'package:navis/settings/settings.dart';
import 'package:navis/worldstate/worldstate.dart';
import 'package:warframestat_repository/warframestat_repository.dart';
Expand All @@ -16,17 +17,35 @@ class BlocBootstrap extends StatefulWidget {
class _BlocBootstrapState extends State<BlocBootstrap> {
late WorldstateCubit _worldstateCubit;
late UserSettingsCubit _userSettingsCubit;
late ArsenalCubit _arsenalCubit;

@override
void initState() {
super.initState();

final worldstateRepo =
RepositoryProvider.of<WarframestatRepository>(context);
final usersettings = RepositoryProvider.of<UserSettings>(context);
final wsRepo = RepositoryProvider.of<WarframestatRepository>(context);
final settings = RepositoryProvider.of<UserSettings>(context);

_worldstateCubit = WorldstateCubit(worldstateRepo);
_userSettingsCubit = UserSettingsCubit(usersettings);
_worldstateCubit = WorldstateCubit(wsRepo);
_userSettingsCubit = UserSettingsCubit(settings);
_arsenalCubit = ArsenalCubit(wsRepo);
}

@override
void didChangeDependencies() {
super.didChangeDependencies();

_worldstateCubit.fetchWorldstate();

final state = _userSettingsCubit.state;
final username = switch (state) {
UserSettingsSuccess() => state.username,
_ => null,
};

if (username != null) {
_arsenalCubit.updateArsenal(username);
}
}

@override
Expand All @@ -35,6 +54,7 @@ class _BlocBootstrapState extends State<BlocBootstrap> {
providers: [
BlocProvider.value(value: _worldstateCubit),
BlocProvider.value(value: _userSettingsCubit),
BlocProvider.value(value: _arsenalCubit),
],
child: widget.child,
);
Expand Down
7 changes: 5 additions & 2 deletions lib/app/widgets/repo_bootstrap.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:drift_flutter/drift_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart';
Expand Down Expand Up @@ -31,8 +32,10 @@ class _RepositoryBootstrapState extends State<RepositoryBootstrap> {
super.initState();

_notifications = NotificationRepository();
_warframestatRepository =
WarframestatRepository(client: SentryHttpClient());
_warframestatRepository = WarframestatRepository(
client: SentryHttpClient(),
database: ArsenalDatabase(driftDatabase(name: 'arsenal')),
);
}

@override
Expand Down
2 changes: 2 additions & 0 deletions lib/arsenal/arsenal.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'cubit/arsenal_cubit.dart';
export 'widgets/widgets.dart';
64 changes: 64 additions & 0 deletions lib/arsenal/cubit/arsenal_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:warframestat_client/warframestat_client.dart';
import 'package:warframestat_repository/warframestat_repository.dart';

part 'arsenal_state.dart';

class ArsenalCubit extends HydratedCubit<ArsenalState> {
ArsenalCubit(this.repository) : super(ArsenalInitial());

final WarframestatRepository repository;

late List<MasteryProgress> _xpInfo;

Future<void> updateArsenal(String username, {bool update = false}) async {
try {
emit(ArsenalUpdating());
await repository.updateArsenalItems(update: update);

_xpInfo = await repository.syncXpInfo(username);

emit(ArsenalSuccess(_xpInfo));
} catch (e, stack) {
debugPrint(e.toString());
emit(ArsenalFailure());

await Sentry.captureException(e, stackTrace: stack);
}
}

@override
ArsenalState? fromJson(Map<String, dynamic> json) {
final inProgess =
List<Map<String, dynamic>>.from(json['inProgress'] as List).map(
(m) => MasteryProgress(
item: MinimalItem.fromJson(m['item'] as Map<String, dynamic>),
xp: m['xp'] as int,
missing: m['missing'] as bool,
),
);

return ArsenalSuccess(inProgess.toList());
}

@override
Map<String, dynamic>? toJson(ArsenalState state) {
if (state is! ArsenalSuccess) return null;

return {
'inProgress': state.inProgress
.map(
(p) => {
'item': p.item.toJson(),
'xp': p.xp,
'missing': p.missing,
},
)
.toList(),
};
}
}
49 changes: 49 additions & 0 deletions lib/arsenal/cubit/arsenal_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
part of 'arsenal_cubit.dart';

sealed class ArsenalState extends Equatable {
const ArsenalState();

@override
List<Object> get props => [];
}

final class ArsenalInitial extends ArsenalState {}

final class ArsenalUpdating extends ArsenalState {}

final class ArsenalSuccess extends ArsenalState {
const ArsenalSuccess(this.xpInfo);

final List<MasteryProgress> xpInfo;

List<MasteryProgress> get inProgress {
return xpInfo.where((i) => i.rank < i.maxRank).toList()
..sort((a, b) {
if (a.rank == 0 && b.rank == 0) return 0;
if (a.rank == 0) return 1;
if (b.rank == 0) return -1;

return a.rank.compareTo(b.rank);
});
}

List<MasteryProgress> get warframes =>
xpInfo.whereNot((i) => i.item.type.isWeapon).toList();

List<MasteryProgress> get weapons =>
xpInfo.where((i) => i.item.type.isWeapon).toList();

List<MasteryProgress> get primaries =>
xpInfo.where((i) => i.item.type.isPrimary).toList();

List<MasteryProgress> get secondary =>
xpInfo.where((i) => i.item.type.isSecondary).toList();

List<MasteryProgress> get melee =>
xpInfo.where((i) => i.item.type.isMelee).toList();

@override
List<Object> get props => [xpInfo];
}

final class ArsenalFailure extends ArsenalState {}
41 changes: 41 additions & 0 deletions lib/arsenal/widgets/arsenal_item.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:navis/utils/item_extensions.dart';
import 'package:navis_ui/navis_ui.dart';
import 'package:warframestat_repository/warframestat_repository.dart';

class ArsenalItemWidget extends StatelessWidget {
const ArsenalItemWidget({super.key, required this.arsenalItem});

final MasteryProgress arsenalItem;

@override
Widget build(BuildContext context) {
const leadingSize = 50.0;

return AppCard(
child: ListTile(
leading: CachedNetworkImage(
imageUrl: arsenalItem.item.imageUrl,
width: leadingSize,
errorWidget: (context, url, error) {
return const Icon(
WarframeSymbols.menu_LotusEmblem,
size: leadingSize,
);
},
),
title: Text(arsenalItem.item.name),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Rank ${arsenalItem.rank}'),
LinearProgressIndicator(
value: arsenalItem.rank / arsenalItem.maxRank,
),
],
),
),
);
}
}
1 change: 1 addition & 0 deletions lib/arsenal/widgets/widgets.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'arsenal_item.dart';
4 changes: 4 additions & 0 deletions lib/codex/cubit/item_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class ItemCubit extends HydratedCubit<ItemState> {

Future<void> fetchItem() async {
final item = await _handleItemFetch(() async => repo.fetchItem(name));
if (item == null) {
emit(const ItemFetchFailure('Item does not exist'));
return;
}

emit(ItemFetchSuccess(item));
}
Expand Down
8 changes: 4 additions & 4 deletions lib/codex/views/codex_search_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ class CodexSearchPage extends StatelessWidget {
context,
),
sliver: SliverAppBar(
titleSpacing: 0,
floating: true,
scrolledUnderElevation: 0,
automaticallyImplyLeading: false,
clipBehavior: Clip.none,
shape: const StadiumBorder(),
scrolledUnderElevation: 0,
titleSpacing: 0,
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
floating: true,
forceElevated: innerBoxIsScrolled,
title: const CodexSearchBar(),
),
Expand Down
2 changes: 1 addition & 1 deletion lib/codex/widgets/codex_entry/gun_stats.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class GunStats extends StatelessWidget {
),
RowItem(
text: Text(l10n.weaponTypeTitle),
child: Text(gun.type.category),
child: Text(gun.type.type),
),
if (gun.polarities?.isNotEmpty ?? false)
RowItem(
Expand Down
2 changes: 1 addition & 1 deletion lib/codex/widgets/codex_entry/melee_stats.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class MeleeStats extends StatelessWidget {
),
RowItem(
text: Text(l10n.weaponTypeTitle),
child: Text(melee.type.category),
child: Text(melee.type.type),
),
if (melee.stancePolarity != null)
RowItem(
Expand Down
25 changes: 22 additions & 3 deletions lib/home/views/home.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:navis/home/widgets/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:navis/home/home.dart';
import 'package:navis/settings/settings.dart';
import 'package:navis_ui/navis_ui.dart';

class HomePage extends StatelessWidget {
Expand All @@ -16,8 +18,25 @@ class HomeView extends StatelessWidget {

@override
Widget build(BuildContext context) {
return ListView(
children: const [NewsSection(), Gaps.gap16, ActivitiesSection()],
return BlocBuilder<UserSettingsCubit, UserSettingsState>(
builder: (context, state) {
final username = switch (state) {
UserSettingsSuccess() => state.username,
_ => null
};

final children = [
const NewsSection(),
const ActivitiesSection(),
if (username != null) const MasteryInProgressSection(),
];

return ListView.separated(
itemCount: children.length,
separatorBuilder: (_, __) => Gaps.gap16,
itemBuilder: (context, index) => children[index],
);
},
);
}
}
Loading
Loading