Skip to content

Commit

Permalink
Merge pull request #639 from lichess-org/fix-perf-stats
Browse files Browse the repository at this point in the history
Fix perf stats
  • Loading branch information
veloce authored Apr 18, 2024
2 parents ebc2b0f + e6344f4 commit 2606d92
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 44 deletions.
34 changes: 20 additions & 14 deletions lib/src/model/common/http.dart
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ extension ClientExtension on Client {
Future<IList<T>> readJsonList<T>(
Uri url, {
Map<String, String>? headers,
required T Function(Map<String, dynamic>) mapper,
required T? Function(Map<String, dynamic>) mapper,
}) async {
final response = await get(url, headers: headers);
_checkResponseSuccess(url, response);
Expand All @@ -243,20 +243,26 @@ extension ClientExtension on Client {
);
}

return IList(
json.map((e) {
if (e is! Map<String, dynamic>) {
_logger.severe('Could not read json object as $T');
throw ClientException('Could not read json object as $T', url);
}
try {
return mapper(e);
} catch (e, st) {
_logger.severe('Could not read json object as $T: $e', e, st);
throw ClientException('Could not read json object as $T: $e', url);
final List<T> list = [];
for (final e in json) {
if (e is! Map<String, dynamic>) {
_logger.severe('Could not read json object as $T: expected an object.');
throw ClientException(
'Could not read json object as $T: expected an object.',
url,
);
}
try {
final mapped = mapper(e);
if (mapped != null) {
list.add(mapped);
}
}),
);
} catch (e, st) {
_logger.severe('Could not read json object as $T: $e', e, st);
throw ClientException('Could not read json object as $T: $e', url);
}
}
return IList(list);
}

/// Sends an HTTP GET request with the given headers to the given URL and
Expand Down
17 changes: 14 additions & 3 deletions lib/src/model/common/perf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ enum Perf {
classical('Classical', 'Classical', LichessIcons.classical),
correspondence('Correspondence', 'Corresp.', LichessIcons.correspondence),
fromPosition('From Position', 'From Pos.', LichessIcons.feather),
chess960('Chess 960', '960', LichessIcons.die_six),
chess960('Chess960', '960', LichessIcons.die_six),
antichess('Antichess', 'Antichess', LichessIcons.antichess),
kingOfTheHill('King Of The Hill', 'KotH', LichessIcons.flag),
kingOfTheHill('King of the Hill', 'KotH', LichessIcons.flag),
threeCheck('Three-check', '3check', LichessIcons.three_check),
atomic('Atomic', 'Atomic', LichessIcons.atom),
horde('Horde', 'Horde', LichessIcons.horde),
Expand Down Expand Up @@ -70,9 +70,15 @@ enum Perf {
}
}

static IMap<String, Perf> nameMap = IMap(Perf.values.asNameMap());
static final IMap<String, Perf> nameMap = IMap(Perf.values.asNameMap());
}

String _titleKey(String title) =>
title.toLowerCase().replaceAll(RegExp('[ -_]'), '');

final IMap<String, Perf> _lowerCaseTitleMap =
Perf.nameMap.map((key, value) => MapEntry(_titleKey(value.title), value));

extension PerfExtension on Pick {
Perf asPerfOrThrow() {
final value = this.required().value;
Expand All @@ -83,6 +89,11 @@ extension PerfExtension on Pick {
if (Perf.nameMap.containsKey(value)) {
return Perf.nameMap[value]!;
}
// handle lichess api inconsistencies
final valueKey = _titleKey(value);
if (_lowerCaseTitleMap.containsKey(valueKey)) {
return _lowerCaseTitleMap[valueKey]!;
}
} else if (value is Map<String, dynamic>) {
final perf = Perf.nameMap[value['key'] as String];
if (perf != null) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/model/user/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ class UserPerfGame with _$UserPerfGame {

@immutable
class UserRatingHistoryPerf {
final String perf;
final Perf perf;
final IList<UserRatingHistoryPoint> points;

const UserRatingHistoryPerf({
Expand Down
16 changes: 11 additions & 5 deletions lib/src/model/user/user_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,23 @@ class UserRepository {
}
}

UserRatingHistoryPerf _ratingHistoryFromJson(
UserRatingHistoryPerf? _ratingHistoryFromJson(
Map<String, dynamic> json,
) =>
_ratingHistoryFromPick(pick(json).required());

UserRatingHistoryPerf _ratingHistoryFromPick(
RequiredPick perf,
UserRatingHistoryPerf? _ratingHistoryFromPick(
RequiredPick pick,
) {
final perf = pick('name').asPerfOrNull();

if (perf == null) {
return null;
}

return UserRatingHistoryPerf(
perf: perf('name').asStringOrThrow(),
points: perf('points').asListOrThrow((point) {
perf: perf,
points: pick('points').asListOrThrow((point) {
final values = point.asListOrThrow((point) => point.asIntOrThrow());
return UserRatingHistoryPoint(
date: DateTime.utc(
Expand Down
3 changes: 2 additions & 1 deletion lib/src/model/user/user_repository_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ Future<IList<UserRatingHistoryPerf>> userRatingHistory(
UserRatingHistoryRef ref, {
required UserId id,
}) async {
return ref.withClient(
return ref.withClientCacheFor(
(client) => UserRepository(client).getRatingHistory(id),
const Duration(minutes: 1),
);
}
2 changes: 1 addition & 1 deletion lib/src/view/account/profile_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class _PerfCards extends ConsumerWidget {
return account.when(
data: (user) {
if (user != null) {
return PerfCards(user: user);
return PerfCards(user: user, isMe: true);
} else {
return const SizedBox.shrink();
}
Expand Down
17 changes: 6 additions & 11 deletions lib/src/view/puzzle/dashboard_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import 'package:lichess_mobile/src/model/auth/auth_session.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_providers.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_theme.dart';
import 'package:lichess_mobile/src/model/user/user.dart';
import 'package:lichess_mobile/src/styles/styles.dart';
import 'package:lichess_mobile/src/utils/l10n_context.dart';
import 'package:lichess_mobile/src/utils/string.dart';
Expand All @@ -21,22 +20,20 @@ import 'package:lichess_mobile/src/widgets/stat_card.dart';
final daysProvider = StateProvider<Days>((ref) => Days.month);

class PuzzleDashboardScreen extends StatelessWidget {
const PuzzleDashboardScreen({super.key, required this.user});

final LightUser user;
const PuzzleDashboardScreen({super.key});

@override
Widget build(BuildContext context) {
return Theme.of(context).platform == TargetPlatform.iOS
? CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
? const CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: SizedBox.shrink(),
trailing: DaysSelector(),
),
child: _Body(user: user),
child: _Body(),
)
: Scaffold(
body: _Body(user: user),
body: const _Body(),
appBar: AppBar(
title: const SizedBox.shrink(),
actions: const [DaysSelector()],
Expand All @@ -46,9 +43,7 @@ class PuzzleDashboardScreen extends StatelessWidget {
}

class _Body extends ConsumerWidget {
const _Body({required this.user});

final LightUser user;
const _Body();

@override
Widget build(BuildContext context, WidgetRef ref) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/view/puzzle/puzzle_tab_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ class _DashboardButton extends ConsumerWidget {
pushPlatformRoute(
context,
title: context.l10n.puzzlePuzzleDashboard,
builder: (_) => PuzzleDashboardScreen(user: session.user),
builder: (_) => const PuzzleDashboardScreen(),
);
}

Expand Down
9 changes: 6 additions & 3 deletions lib/src/view/user/perf_cards.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import 'package:lichess_mobile/src/widgets/platform.dart';
import 'package:lichess_mobile/src/widgets/rating.dart';

class PerfCards extends StatelessWidget {
const PerfCards({required this.user, super.key});
const PerfCards({required this.user, required this.isMe, super.key});

final User user;

final bool isMe;

@override
Widget build(BuildContext context) {
List<Perf> userPerfs = Perf.values.where((element) {
Expand Down Expand Up @@ -50,7 +52,8 @@ class PerfCards extends StatelessWidget {
itemBuilder: (context, index) {
final perf = userPerfs[index];
final userPerf = user.perfs[perf]!;
final bool isPerfWithoutStats = Perf.streak == perf;
final bool isPerfWithoutStats =
Perf.streak == perf || (!isMe && Perf.puzzle == perf);
return SizedBox(
height: 100,
width: 100,
Expand Down Expand Up @@ -126,7 +129,7 @@ class PerfCards extends StatelessWidget {
case Perf.storm:
return StormDashboardModal(user: user.lightUser);
case Perf.puzzle:
return PuzzleDashboardScreen(user: user.lightUser);
return const PuzzleDashboardScreen();
default:
return PerfStatsScreen(
user: user,
Expand Down
6 changes: 3 additions & 3 deletions lib/src/view/user/perf_stats_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@ class _Body extends ConsumerWidget {
ratingHistory.when(
data: (ratingHistoryData) {
final ratingHistoryPerfData = ratingHistoryData
.where((element) => element.perf == perf.title)
.first;
.firstWhereOrNull((element) => element.perf == perf);

if (ratingHistoryPerfData.points.isEmpty) {
if (ratingHistoryPerfData == null ||
ratingHistoryPerfData.points.isEmpty) {
return const SizedBox.shrink();
}
return _EloChart(ratingHistoryPerfData);
Expand Down
2 changes: 1 addition & 1 deletion lib/src/view/user/user_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class _UserProfileListView extends StatelessWidget {
return ListView(
children: [
UserProfile(user: user),
PerfCards(user: user),
PerfCards(user: user, isMe: false),
UserActivityWidget(user: user),
RecentGames(user: user.lightUser),
],
Expand Down

0 comments on commit 2606d92

Please sign in to comment.