Skip to content

Commit 52f7eaf

Browse files
authored
feat(hydrated_bloc)!: support for WASM (#4313)
1 parent ed7b27c commit 52f7eaf

File tree

7 files changed

+66
-45
lines changed

7 files changed

+66
-45
lines changed

packages/hydrated_bloc/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ Future<void> main() async {
6565
WidgetsFlutterBinding.ensureInitialized();
6666
HydratedBloc.storage = await HydratedStorage.build(
6767
storageDirectory: kIsWeb
68-
? HydratedStorage.webStorageDirectory
69-
: await getApplicationDocumentsDirectory(),
68+
? HydratedStorageDirectory.web
69+
: HydratedStorageDirectory((await getTemporaryDirectory()).path),
7070
);
7171
runApp(App());
7272
}

packages/hydrated_bloc/example/lib/main.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ void main() async {
99
WidgetsFlutterBinding.ensureInitialized();
1010
HydratedBloc.storage = await HydratedStorage.build(
1111
storageDirectory: kIsWeb
12-
? HydratedStorage.webStorageDirectory
13-
: await getTemporaryDirectory(),
12+
? HydratedStorageDirectory.web
13+
: HydratedStorageDirectory((await getTemporaryDirectory()).path),
1414
);
1515
runApp(const App());
1616
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import 'dart:convert';
2+
import 'dart:io';
3+
4+
import 'package:hive_ce/hive.dart';
5+
6+
/// The storage migration implementation when using dart:io.
7+
Future<dynamic> migrate(String directory, Box<dynamic> box) async {
8+
final file = File('$directory/.hydrated_bloc.json');
9+
if (file.existsSync()) {
10+
try {
11+
final dynamic storageJson = json.decode(await file.readAsString());
12+
final cache = (storageJson as Map).cast<String, String>();
13+
for (final key in cache.keys) {
14+
try {
15+
final string = cache[key];
16+
final dynamic object = json.decode(string ?? '');
17+
await box.put(key, object);
18+
} catch (_) {}
19+
}
20+
} catch (_) {}
21+
await file.delete();
22+
}
23+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import 'package:hive_ce/hive.dart';
2+
3+
/// The storage migration implementation stub.
4+
Future<dynamic> migrate(String directory, Box<dynamic> box) async {}

packages/hydrated_bloc/lib/src/hydrated_storage.dart

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import 'dart:async';
2-
import 'dart:convert';
3-
import 'dart:io';
42

53
import 'package:hive_ce/hive.dart';
64
// ignore: implementation_imports
75
import 'package:hive_ce/src/hive_impl.dart';
6+
import 'package:hydrated_bloc/src/_migration/_migration_stub.dart'
7+
if (dart.library.io) 'package:hydrated_bloc/src/_migration/_migration_io.dart';
88
import 'package:hydrated_bloc/src/hydrated_cipher.dart';
99
import 'package:meta/meta.dart';
1010
import 'package:synchronized/synchronized.dart';
@@ -28,29 +28,40 @@ abstract class Storage {
2828
Future<void> close();
2929
}
3030

31-
/// {@template hydrated_storage}
32-
/// Implementation of [Storage] which uses [package:hive](https://pub.dev/packages/hive)
33-
/// to persist and retrieve state changes from the local device.
31+
/// {@template hydrated_storage_directory}
32+
/// A platform-agnostic storage directory representation.
3433
/// {@endtemplate}
35-
class HydratedStorage implements Storage {
36-
/// {@macro hydrated_storage}
37-
@visibleForTesting
38-
HydratedStorage(this._box);
34+
class HydratedStorageDirectory {
35+
/// {@macro hydrated_storage_directory}
36+
const HydratedStorageDirectory(this.path);
37+
38+
/// The path to the storage directory.
39+
final String path;
3940

4041
/// Sentinel directory used to determine that web storage should be used
4142
/// when initializing [HydratedStorage].
4243
///
4344
/// ```dart
4445
/// await HydratedStorage.build(
45-
/// storageDirectory: HydratedStorage.webStorageDirectory,
46+
/// storageDirectory: HydratedStorageDirectory.web,
4647
/// );
4748
/// ```
48-
static final webStorageDirectory = Directory('');
49+
static const web = HydratedStorageDirectory('');
50+
}
51+
52+
/// {@template hydrated_storage}
53+
/// Implementation of [Storage] which uses [package:hive_ce](https://pub.dev/packages/hive_ce)
54+
/// to persist and retrieve state changes from the local device.
55+
/// {@endtemplate}
56+
class HydratedStorage implements Storage {
57+
/// {@macro hydrated_storage}
58+
@visibleForTesting
59+
HydratedStorage(this._box);
4960

5061
/// Returns an instance of [HydratedStorage].
5162
/// [storageDirectory] is required.
5263
///
53-
/// For web, use [webStorageDirectory] as the `storageDirectory`
64+
/// For web, use [HydratedStorageDirectory.web] as the `storageDirectory`
5465
///
5566
/// ```dart
5667
/// import 'package:flutter/foundation.dart';
@@ -63,8 +74,8 @@ class HydratedStorage implements Storage {
6374
/// WidgetsFlutterBinding.ensureInitialized();
6475
/// HydratedBloc.storage = await HydratedStorage.build(
6576
/// storageDirectory: kIsWeb
66-
/// ? HydratedStorage.webStorageDirectory
67-
/// : await getTemporaryDirectory(),
77+
/// ? HydratedStorageDirectory.web
78+
/// : HydratedStorageDirectory((await getTemporaryDirectory()).path),
6879
/// );
6980
/// runApp(App());
7081
/// }
@@ -81,7 +92,7 @@ class HydratedStorage implements Storage {
8192
/// return HydratedAesCipher(byteskey);
8293
/// ```
8394
static Future<HydratedStorage> build({
84-
required Directory storageDirectory,
95+
required HydratedStorageDirectory storageDirectory,
8596
HydratedCipher? encryptionCipher,
8697
}) {
8798
return _lock.synchronized(() async {
@@ -91,7 +102,7 @@ class HydratedStorage implements Storage {
91102
hive = HiveImpl();
92103
Box<dynamic> box;
93104

94-
if (storageDirectory == webStorageDirectory) {
105+
if (storageDirectory == HydratedStorageDirectory.web) {
95106
box = await hive.openBox<dynamic>(
96107
'hydrated_box',
97108
encryptionCipher: encryptionCipher,
@@ -102,31 +113,13 @@ class HydratedStorage implements Storage {
102113
'hydrated_box',
103114
encryptionCipher: encryptionCipher,
104115
);
105-
await _migrate(storageDirectory, box);
116+
await migrate(storageDirectory.path, box);
106117
}
107118

108119
return _instance = HydratedStorage(box);
109120
});
110121
}
111122

112-
static Future<dynamic> _migrate(Directory directory, Box<dynamic> box) async {
113-
final file = File('${directory.path}/.hydrated_bloc.json');
114-
if (file.existsSync()) {
115-
try {
116-
final dynamic storageJson = json.decode(await file.readAsString());
117-
final cache = (storageJson as Map).cast<String, String>();
118-
for (final key in cache.keys) {
119-
try {
120-
final string = cache[key];
121-
final dynamic object = json.decode(string ?? '');
122-
await box.put(key, object);
123-
} catch (_) {}
124-
}
125-
} catch (_) {}
126-
await file.delete();
127-
}
128-
}
129-
130123
/// Internal instance of [HiveImpl].
131124
/// It should only be used for testing.
132125
@visibleForTesting

packages/hydrated_bloc/test/e2e_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void main() {
1515

1616
setUp(() async {
1717
storage = await HydratedStorage.build(
18-
storageDirectory: Directory(
18+
storageDirectory: HydratedStorageDirectory(
1919
path.join(Directory.current.path, '.cache'),
2020
),
2121
);

packages/hydrated_bloc/test/hydrated_storage_test.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class MockBox extends Mock implements Box<dynamic> {}
1515
void main() {
1616
group('HydratedStorage', () {
1717
final cwd = Directory.current.absolute.path;
18-
final storageDirectory = Directory(cwd);
18+
final storageDirectory = HydratedStorageDirectory(cwd);
1919

2020
late Storage storage;
2121

@@ -78,7 +78,7 @@ void main() {
7878
await runZonedGuarded(
7979
() {
8080
HydratedStorage.build(
81-
storageDirectory: HydratedStorage.webStorageDirectory,
81+
storageDirectory: HydratedStorageDirectory.web,
8282
).whenComplete(completer.complete);
8383
return completer.future;
8484
},
@@ -179,7 +179,7 @@ void main() {
179179
test('writes key/value pairs correctly', () async {
180180
const token = 'token';
181181
storage = await HydratedStorage.build(
182-
storageDirectory: Directory(cwd),
182+
storageDirectory: HydratedStorageDirectory(cwd),
183183
);
184184
await Stream.fromIterable(
185185
Iterable.generate(120, (i) => i),
@@ -193,7 +193,7 @@ void main() {
193193
storage.write(token, record);
194194

195195
storage = await HydratedStorage.build(
196-
storageDirectory: Directory(cwd),
196+
storageDirectory: HydratedStorageDirectory(cwd),
197197
);
198198

199199
final written = storage.read(token) as List<List<String>>;
@@ -217,8 +217,9 @@ void main() {
217217

218218
test('Hive and Hydrated default directories', () async {
219219
Hive.init(docs);
220+
final tempDir = Directory(temp)..createSync();
220221
storage = await HydratedStorage.build(
221-
storageDirectory: Directory(temp)..createSync(),
222+
storageDirectory: HydratedStorageDirectory(tempDir.path),
222223
);
223224

224225
var box = await Hive.openBox<String>('hive');

0 commit comments

Comments
 (0)