-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathdatabase_provider.dart
More file actions
113 lines (97 loc) · 3.38 KB
/
database_provider.dart
File metadata and controls
113 lines (97 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mixin_logger/mixin_logger.dart';
import '../../crypto/crypto_key_value.dart';
import '../../db/app/app_database.dart';
import '../../db/database.dart';
import '../../db/fts_database.dart';
import '../../db/mixin_database.dart';
import '../../utils/rivepod.dart';
import '../../utils/synchronized.dart';
import 'account/multi_auth_provider.dart';
import 'hive_key_value_provider.dart';
import 'slide_category_provider.dart';
final appDatabaseProvider = StateProvider<AppDatabase>(
(ref) {
final db = AppDatabase.connect(fromMainIsolate: true);
ref.onDispose(db.close);
return db;
},
);
final appDatabaseInitErrorProvider = StateProvider<dynamic>((ref) => null);
final databaseProvider =
StateNotifierProvider.autoDispose<DatabaseOpener, AsyncValue<Database>>(
(ref) {
final identityNumber =
ref.watch(authAccountProvider.select((value) => value?.identityNumber));
if (identityNumber == null) return DatabaseOpener(ref);
return DatabaseOpener.open(identityNumber, ref);
},
);
extension _DatabaseExt on MixinDatabase {
Future<void> doInitVerify() =>
conversationDao.conversationCountByCategory(SlideCategoryType.chats);
}
class DatabaseOpener extends DistinctStateNotifier<AsyncValue<Database>> {
DatabaseOpener(this.ref) : super(const AsyncValue.loading());
DatabaseOpener.open(this.identityNumber, this.ref)
: super(const AsyncValue.loading()) {
open();
}
String? identityNumber;
final Ref ref;
final Lock _lock = Lock();
Future<void> open() => _lock.synchronized(() async {
final identityNumber = this.identityNumber!;
d('connect to database: $identityNumber');
if (state.hasValue) {
e('database already opened');
return;
}
try {
final mixinDatabase =
await connectToDatabase(identityNumber, fromMainIsolate: true);
final db = Database(
mixinDatabase,
await FtsDatabase.connect(identityNumber, fromMainIsolate: true),
);
// Do a database query, to ensure database has properly initialized.
await mixinDatabase.doInitVerify();
try {
await _onDatabaseOpenSucceed(db, identityNumber);
} catch (error, stacktrace) {
e('_onDatabaseOpenSucceed has error: $error, $stacktrace');
}
state = AsyncValue.data(db);
} catch (error, stacktrace) {
e('failed to open database: $error, $stacktrace');
state = AsyncValue.error(error, stacktrace);
}
});
Future<void> _onDatabaseOpenSucceed(
Database database, String identityNumber) async {
// migrate old crypto key value to new crypto key value
try {
final hive = ref.read(hiveProvider(identityNumber));
final oldCryptoKeyValue = CryptoKeyValue();
await oldCryptoKeyValue.migrateToNewCryptoKeyValue(
hive,
identityNumber,
database.cryptoKeyValue,
);
} catch (error, stacktrace) {
e('migrateToNewCryptoKeyValue has error: $error, $stacktrace');
}
}
@override
Future<void> dispose() async {
await close();
super.dispose();
}
Future<void> close() async {
if (identityNumber != null) {
i('close database: $identityNumber');
}
await state.valueOrNull?.dispose();
state = const AsyncValue.loading();
}
}