fluffychat/lib/pangea/analytics_data/analytics_database_builder.dart
2025-12-23 14:35:41 -05:00

95 lines
2.8 KiB
Dart

import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:universal_html/html.dart' as html;
import 'package:fluffychat/pangea/analytics_data/analytics_database.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/sqlcipher_stub.dart';
import 'package:fluffychat/utils/platform_infos.dart';
Future<AnalyticsDatabase> analyticsDatabaseBuilder(String name) async {
AnalyticsDatabase? database;
try {
database = await _constructDatabase(name);
await database.open();
return database;
} catch (e, s) {
ErrorHandler.logError(
e: e,
s: s,
data: {"clientID": name},
m: "Failed to open analytics database. Opening fallback database.",
);
Logs().wtf('Unable to construct database!', e, s);
// Try to delete database so that it can created again on next init:
database?.delete().catchError((err, s) {
ErrorHandler.logError(
e: e,
s: s,
data: {},
m: "Failed to delete analytics database after failed construction.",
);
});
// Delete database file:
if (database == null && !kIsWeb) {
final dbFile = File(await _getDatabasePath(name));
if (await dbFile.exists()) await dbFile.delete();
}
rethrow;
}
}
Future<AnalyticsDatabase> _constructDatabase(String name) async {
if (kIsWeb) {
html.window.navigator.storage?.persist();
return await AnalyticsDatabase.init(name);
}
Directory? fileStorageLocation;
try {
fileStorageLocation = await getTemporaryDirectory();
} on MissingPlatformDirectoryException catch (_) {
Logs().w(
'No temporary directory for file cache available on this platform.',
);
}
final path = await _getDatabasePath(name);
// fix dlopen for old Android
await applyWorkaroundToOpenSqlCipherOnOldAndroidVersions();
// import the SQLite / SQLCipher shared objects / dynamic libraries
final factory =
createDatabaseFactoryFfi(ffiInit: SQfLiteEncryptionHelper.ffiInit);
// required for [getDatabasesPath]
databaseFactory = factory;
final database = await factory.openDatabase(
path,
options: OpenDatabaseOptions(version: 1),
);
return await AnalyticsDatabase.init(
name,
database: database,
fileStorageLocation: fileStorageLocation?.uri,
deleteFilesAfterDuration: const Duration(days: 30),
);
}
Future<String> _getDatabasePath(String name) async {
final databaseDirectory = PlatformInfos.isIOS || PlatformInfos.isMacOS
? await getLibraryDirectory()
: await getApplicationSupportDirectory();
return join(databaseDirectory.path, '$name.sqlite');
}