Skip to content

Commit

Permalink
feat: add methods for saving unfinished puzzle
Browse files Browse the repository at this point in the history
  • Loading branch information
thisissandipp committed Jul 30, 2024
1 parent 81b15e9 commit a23d280
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 70 deletions.
36 changes: 26 additions & 10 deletions lib/main_development.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sudoku/api/api.dart';
import 'package:sudoku/app/app.dart';
import 'package:sudoku/bootstrap.dart';
import 'package:sudoku/cache/cache.dart';
import 'package:sudoku/env/env.dart';
import 'package:sudoku/repository/repository.dart';
import 'package:sudoku/storage/storage.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();

unawaited(
bootstrap(() async {
final apiClient = SudokuDioClient(baseUrl: Env.apiBaseUrl);
final cacheClient = CacheClient();

void main() {
bootstrap(() {
final apiClient = SudokuDioClient(baseUrl: Env.apiBaseUrl);
final storageClient = LocalStorageClient(
plugin: await SharedPreferences.getInstance(),
);

final cacheClient = CacheClient();
final puzzleRepository = PuzzleRepository(cacheClient: cacheClient);
final puzzleRepository = PuzzleRepository(
cacheClient: cacheClient,
storageClient: storageClient,
);

return App(
apiClient: apiClient,
puzzleRepository: puzzleRepository,
);
});
return App(
apiClient: apiClient,
puzzleRepository: puzzleRepository,
);
}),
);
}
36 changes: 26 additions & 10 deletions lib/main_production.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sudoku/api/api.dart';
import 'package:sudoku/app/app.dart';
import 'package:sudoku/bootstrap.dart';
import 'package:sudoku/cache/cache.dart';
import 'package:sudoku/env/env.dart';
import 'package:sudoku/repository/repository.dart';
import 'package:sudoku/storage/storage.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();

unawaited(
bootstrap(() async {
final apiClient = SudokuDioClient(baseUrl: Env.apiBaseUrl);
final cacheClient = CacheClient();

void main() {
bootstrap(() {
final apiClient = SudokuDioClient(baseUrl: Env.apiBaseUrl);
final storageClient = LocalStorageClient(
plugin: await SharedPreferences.getInstance(),
);

final cacheClient = CacheClient();
final puzzleRepository = PuzzleRepository(cacheClient: cacheClient);
final puzzleRepository = PuzzleRepository(
cacheClient: cacheClient,
storageClient: storageClient,
);

return App(
apiClient: apiClient,
puzzleRepository: puzzleRepository,
);
});
return App(
apiClient: apiClient,
puzzleRepository: puzzleRepository,
);
}),
);
}
36 changes: 26 additions & 10 deletions lib/main_staging.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sudoku/api/api.dart';
import 'package:sudoku/app/app.dart';
import 'package:sudoku/bootstrap.dart';
import 'package:sudoku/cache/cache.dart';
import 'package:sudoku/env/env.dart';
import 'package:sudoku/repository/repository.dart';
import 'package:sudoku/storage/storage.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();

unawaited(
bootstrap(() async {
final apiClient = SudokuDioClient(baseUrl: Env.apiBaseUrl);
final cacheClient = CacheClient();

void main() {
bootstrap(() {
final apiClient = SudokuDioClient(baseUrl: Env.apiBaseUrl);
final storageClient = LocalStorageClient(
plugin: await SharedPreferences.getInstance(),
);

final cacheClient = CacheClient();
final puzzleRepository = PuzzleRepository(cacheClient: cacheClient);
final puzzleRepository = PuzzleRepository(
cacheClient: cacheClient,
storageClient: storageClient,
);

return App(
apiClient: apiClient,
puzzleRepository: puzzleRepository,
);
});
return App(
apiClient: apiClient,
puzzleRepository: puzzleRepository,
);
}),
);
}
34 changes: 27 additions & 7 deletions lib/repository/puzzle_repository.dart
Original file line number Diff line number Diff line change
@@ -1,35 +1,55 @@
import 'package:flutter/widgets.dart';
import 'package:sudoku/cache/cache.dart';
import 'package:sudoku/puzzle/puzzle.dart';
import 'package:sudoku/storage/storage.dart';

/// {@template puzzle_repository}
/// A repository that handles `puzzle` related data.
///
/// Used to pass data from home page to puzzle page.
/// Used to pass data from home page to puzzle page, and to save and
/// retrieve unfinished puzzle from local memory.
/// {@endtemplate}
class PuzzleRepository {
/// {@macro puzzle_repository}
const PuzzleRepository({
required CacheClient cacheClient,
}) : _cacheClient = cacheClient;
required StorageAPI storageClient,
}) : _cacheClient = cacheClient,
_storageClient = storageClient;

final CacheClient _cacheClient;

/// The key used for storing the puzzle in-memory.
final StorageAPI _storageClient;

/// The key used for storing the puzzle in cache.
///
/// This is only exposed for testing and shouldn't be used by consumers of
/// this library.
@visibleForTesting
static const kPuzzleKey = '__puzzle_key__';

/// Provides the puzzle stored in-memory.
/// Provides the puzzle stored in cache.
///
/// Returns null, if there is no puzzle.
Puzzle? getPuzzle() => _cacheClient.read<Puzzle>(key: kPuzzleKey);
Puzzle? fetchPuzzleFromCache() => _cacheClient.read<Puzzle>(key: kPuzzleKey);

/// Saves a puzzle in-memory.
/// Saves a puzzle in cache.
///
/// If there's already a puzzle there, it will be replaced.
void storePuzzle({required Puzzle puzzle}) =>
void savePuzzleToCache({required Puzzle puzzle}) =>
_cacheClient.write<Puzzle>(key: kPuzzleKey, value: puzzle);

/// Emits the puzzle stored in local storage. If there's not puzzle,
/// it simply emits null.
Stream<Puzzle?> getPuzzleFromLocalMemory() => _storageClient.getPuzzle();

/// Stores the puzzle in local storage. The aim is to save unfinished puzzle.
Future<void> storePuzzleInLocalMemory({required Puzzle puzzle}) =>
_storageClient.storePuzzle(puzzle: puzzle);

/// Clears out the local storage, after an unfinished puzzle is completed.
Future<void> clearPuzzleInLocalMemory() => _storageClient.clearPuzzleStore();

/// Closed any resources used during the above operations.
Future<void> close() => _storageClient.close();
}
35 changes: 26 additions & 9 deletions lib/storage/local_storage_client.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:convert';

import 'package:flutter/widgets.dart';
import 'package:rxdart/rxdart.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sudoku/puzzle/models/puzzle.dart';
import 'package:sudoku/storage/storage.dart';
Expand All @@ -10,12 +11,27 @@ import 'package:sudoku/storage/storage.dart';
/// {@endtemplate}
class LocalStorageClient extends StorageAPI {
/// {@macro local_storage_client}
const LocalStorageClient({
LocalStorageClient({
required SharedPreferences plugin,
}) : _plugin = plugin;
}) : _plugin = plugin {
_initializeStreamController();
}

final SharedPreferences _plugin;

final _puzzleStreamController = BehaviorSubject<Puzzle?>.seeded(null);

void _initializeStreamController() {
final source = _getValue(kPuzzleStorageKey);
if (source != null) {
final jsonMap = json.decode(source) as Map<String, dynamic>;
final puzzle = Puzzle.fromJson(jsonMap);
_puzzleStreamController.add(puzzle);
} else {
_puzzleStreamController.add(null);
}
}

/// The key used for storing the puzzle locally.
///
/// This is only exposed for testing and shouldn't be used by consumers.
Expand All @@ -28,22 +44,23 @@ class LocalStorageClient extends StorageAPI {
_plugin.setString(key, value);

@override
Future<Puzzle?> getPuzzle() async {
final source = _getValue(kPuzzleStorageKey);
if (source == null) return null;

final jsonMap = json.decode(source) as Map<String, dynamic>;
return Puzzle.fromJson(jsonMap);
}
Stream<Puzzle?> getPuzzle() => _puzzleStreamController.asBroadcastStream();

@override
Future<void> storePuzzle({required Puzzle puzzle}) async {
_puzzleStreamController.add(puzzle);
return _setValue(kPuzzleStorageKey, json.encode(puzzle));
}

@override
Future<void> clearPuzzleStore() async {
_puzzleStreamController.add(null);
await _plugin.remove(kPuzzleStorageKey);
return;
}

@override
Future<void> close() {
return _puzzleStreamController.close();
}
}
9 changes: 6 additions & 3 deletions lib/storage/storage_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ abstract class StorageAPI {
/// If a [puzzle] exists, it will be replaced.
Future<void> storePuzzle({required Puzzle puzzle});

/// Provides the stored puzzle.
/// Provides a [Stream] of the stored puzzle.
///
/// Returns null, if there is no puzzle stored.
Future<Puzzle?> getPuzzle();
/// Emits null, if there is no puzzle stored.
Stream<Puzzle?> getPuzzle();

/// Clears the stored puzzle.
Future<void> clearPuzzleStore();

/// Closes the client and frees up any resources.
Future<void> close();
}
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.0"
rxdart:
dependency: "direct main"
description:
name: rxdart
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
url: "https://pub.dev"
source: hosted
version: "0.28.0"
shared_preferences:
dependency: "direct main"
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies:
intl: ^0.19.0
json_annotation: ^4.9.0
loading_indicator: ^3.1.1
rxdart: ^0.28.0
shared_preferences: ^2.2.3

dev_dependencies:
Expand Down
Loading

0 comments on commit a23d280

Please sign in to comment.