Skip to content

Commit

Permalink
Merge pull request #1212 from tom-anders/streakStoreScore
Browse files Browse the repository at this point in the history
save streak score locally
  • Loading branch information
veloce authored Dec 9, 2024
2 parents 5149701 + c33629f commit ffbe8bd
Show file tree
Hide file tree
Showing 6 changed files with 438 additions and 14 deletions.
13 changes: 11 additions & 2 deletions lib/src/model/puzzle/puzzle_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import 'package:lichess_mobile/src/model/puzzle/puzzle_service.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_session.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_streak.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_theme.dart';
import 'package:lichess_mobile/src/model/puzzle/streak_storage.dart';
import 'package:lichess_mobile/src/network/http.dart';
import 'package:lichess_mobile/src/utils/rate_limit.dart';
import 'package:result_extensions/result_extensions.dart';
Expand Down Expand Up @@ -273,7 +274,15 @@ class PuzzleController extends _$PuzzleController {
state = _loadNewContext(nextContext, nextStreak ?? state.streak);
}

void sendStreakResult() {
void saveStreakResultLocally() {
ref.read(streakStorageProvider(initialContext.userId)).saveActiveStreak(
state.streak!,
);
}

void _sendStreakResult() {
ref.read(streakStorageProvider(initialContext.userId)).clearActiveStreak();

if (initialContext.userId != null) {
final streak = state.streak?.index;
if (streak != null && streak > 0) {
Expand Down Expand Up @@ -420,7 +429,7 @@ class PuzzleController extends _$PuzzleController {
finished: true,
),
);
sendStreakResult();
_sendStreakResult();
} else {
if (_nextPuzzleFuture == null) {
assert(false, 'next puzzle future cannot be null with streak');
Expand Down
37 changes: 35 additions & 2 deletions lib/src/model/puzzle/puzzle_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import 'package:lichess_mobile/src/model/puzzle/puzzle_opening.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_repository.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_service.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_storage.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_streak.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_theme.dart';
import 'package:lichess_mobile/src/model/puzzle/storm.dart';
import 'package:lichess_mobile/src/model/puzzle/streak_storage.dart';
import 'package:lichess_mobile/src/network/http.dart';
import 'package:lichess_mobile/src/utils/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
Expand All @@ -35,9 +37,40 @@ Future<PuzzleContext?> nextPuzzle(Ref ref, PuzzleAngle angle) async {
);
}

typedef InitialStreak = ({
PuzzleStreak streak,
Puzzle puzzle,
});

/// Fetches the active streak from the local storage if available, otherwise fetches it from the server.
@riverpod
Future<PuzzleStreakResponse> streak(Ref ref) {
return ref.withClient((client) => PuzzleRepository(client).streak());
Future<InitialStreak> streak(Ref ref) async {
final session = ref.watch(authSessionProvider);
final streakStorage = ref.watch(streakStorageProvider(session?.user.id));
final activeStreak = await streakStorage.loadActiveStreak();
if (activeStreak != null) {
final puzzle = await ref
.read(puzzleProvider(activeStreak.streak[activeStreak.index]).future);

return (
streak: activeStreak,
puzzle: puzzle,
);
}

final rsp =
await ref.withClient((client) => PuzzleRepository(client).streak());

return (
streak: PuzzleStreak(
streak: rsp.streak,
index: 0,
hasSkipped: false,
finished: false,
timestamp: rsp.timestamp,
),
puzzle: rsp.puzzle,
);
}

@riverpod
Expand Down
6 changes: 5 additions & 1 deletion lib/src/model/puzzle/puzzle_streak.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/src/model/common/id.dart';

part 'puzzle_streak.g.dart';
part 'puzzle_streak.freezed.dart';

typedef Streak = IList<PuzzleId>;

@freezed
@Freezed(fromJson: true, toJson: true)
class PuzzleStreak with _$PuzzleStreak {
const PuzzleStreak._();

Expand All @@ -19,4 +20,7 @@ class PuzzleStreak with _$PuzzleStreak {
}) = _PuzzleStreak;

PuzzleId? get nextId => streak.getOrNull(index + 1);

factory PuzzleStreak.fromJson(Map<String, dynamic> json) =>
_$PuzzleStreakFromJson(json);
}
49 changes: 49 additions & 0 deletions lib/src/model/puzzle/streak_storage.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'dart:convert';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/src/binding.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
import 'package:lichess_mobile/src/model/puzzle/puzzle_streak.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shared_preferences/shared_preferences.dart';

part 'streak_storage.g.dart';

@Riverpod(keepAlive: true)
StreakStorage streakStorage(Ref ref, UserId? userId) {
return StreakStorage(userId);
}

/// Local storage for the current puzzle streak.
class StreakStorage {
const StreakStorage(this.userId);
final UserId? userId;

Future<PuzzleStreak?> loadActiveStreak() async {
final stored = _store.getString(_storageKey);
if (stored == null) {
return null;
}

return PuzzleStreak.fromJson(
jsonDecode(stored) as Map<String, dynamic>,
);
}

Future<void> saveActiveStreak(PuzzleStreak streak) async {
await _store.setString(
_storageKey,
jsonEncode(streak),
);
}

Future<void> clearActiveStreak() async {
await _store.remove(_storageKey);
}

SharedPreferencesWithCache get _store =>
LichessBinding.instance.sharedPreferences;

String get _storageKey => 'puzzle_streak.${userId ?? '**anon**'}';
}
13 changes: 4 additions & 9 deletions lib/src/view/puzzle/streak_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,7 @@ class _Load extends ConsumerWidget {
angle: const PuzzleTheme(PuzzleThemeKey.mix),
userId: session?.user.id,
),
streak: PuzzleStreak(
streak: data.streak,
index: 0,
hasSkipped: false,
finished: false,
timestamp: data.timestamp,
),
streak: data.streak,
);
},
loading: () => const Center(child: CircularProgressIndicator.adaptive()),
Expand Down Expand Up @@ -219,9 +213,10 @@ class _Body extends ConsumerWidget {
context: context,
builder: (context) => YesNoDialog(
title: Text(context.l10n.mobileAreYouSure),
content: Text(context.l10n.mobilePuzzleStreakAbortWarning),
content:
const Text('No worries, your score will be saved locally.'),
onYes: () {
ref.read(ctrlProvider.notifier).sendStreakResult();
ref.read(ctrlProvider.notifier).saveStreakResultLocally();
return Navigator.of(context).pop(true);
},
onNo: () => Navigator.of(context).pop(false),
Expand Down
Loading

0 comments on commit ffbe8bd

Please sign in to comment.