replace globals with app environment

This commit is contained in:
otsmr 2026-04-21 02:15:31 +02:00
parent 693c74df46
commit d9f9f7645e
15 changed files with 48 additions and 45 deletions

View file

@ -1,17 +1,28 @@
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/services/api.service.dart'; import 'package:twonly/src/services/api.service.dart';
import 'package:twonly/src/services/subscription.service.dart'; import 'package:twonly/src/services/subscription.service.dart';
class AppEnvironment {
static late final String cacheDir;
static late final String supportDir;
static late final List<CameraDescription> cameras;
static Future<void> init() async {
cacheDir = (await getApplicationCacheDirectory()).path;
supportDir = (await getApplicationSupportDirectory()).path;
cameras = await availableCameras();
}
}
late ApiService apiService; late ApiService apiService;
// uses for background notification // uses for background notification
late TwonlyDB twonlyDB; late TwonlyDB twonlyDB;
List<CameraDescription> gCameras = <CameraDescription>[];
// Cached UserData in the memory. Every time the user data is changed the `updateUserdata` function is called, // Cached UserData in the memory. Every time the user data is changed the `updateUserdata` function is called,
// which will update this global variable. The variable is set in the main.dart and after the user has registered in the register.view.dart // which will update this global variable. The variable is set in the main.dart and after the user has registered in the register.view.dart
late UserData gUser; late UserData gUser;
@ -36,8 +47,5 @@ bool globalIsInBackgroundTask = false;
bool globalAllowErrorTrackingViaSentry = false; bool globalAllowErrorTrackingViaSentry = false;
bool globalGotMessageFromServer = false; bool globalGotMessageFromServer = false;
late String globalApplicationCacheDirectory;
late String globalApplicationSupportDirectory;
final GlobalKey<ScaffoldMessengerState> globalRootScaffoldMessengerKey = final GlobalKey<ScaffoldMessengerState> globalRootScaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState>(); GlobalKey<ScaffoldMessengerState>();

View file

@ -1,11 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:twonly/app.dart'; import 'package:twonly/app.dart';
@ -35,10 +33,7 @@ void main() async {
SentryWidgetsFlutterBinding.ensureInitialized(); SentryWidgetsFlutterBinding.ensureInitialized();
await RustLib.init(); await RustLib.init();
await AppEnvironment.init();
globalApplicationCacheDirectory = (await getApplicationCacheDirectory()).path;
globalApplicationSupportDirectory =
(await getApplicationSupportDirectory()).path;
initLogger(); initLogger();
@ -46,8 +41,8 @@ void main() async {
await bridge.initializeTwonlyFlutter( await bridge.initializeTwonlyFlutter(
config: bridge.TwonlyConfig( config: bridge.TwonlyConfig(
databasePath: '$globalApplicationSupportDirectory/twonly.sqlite', databasePath: '${AppEnvironment.supportDir}/twonly.sqlite',
dataDirectory: globalApplicationSupportDirectory, dataDirectory: AppEnvironment.supportDir,
), ),
); );
@ -56,7 +51,7 @@ void main() async {
var user = await getUser(); var user = await getUser();
if (Platform.isIOS && user != null) { if (Platform.isIOS && user != null) {
final db = File('$globalApplicationSupportDirectory/twonly.sqlite'); final db = File('${AppEnvironment.supportDir}/twonly.sqlite');
if (!db.existsSync()) { if (!db.existsSync()) {
Log.error('[twonly] IOS: App was removed and then reinstalled again...'); Log.error('[twonly] IOS: App was removed and then reinstalled again...');
await const FlutterSecureStorage().deleteAll(); await const FlutterSecureStorage().deleteAll();
@ -92,8 +87,6 @@ void main() async {
unawaited(setupPushNotification()); unawaited(setupPushNotification());
gCameras = await availableCameras();
apiService = ApiService(); apiService = ApiService();
twonlyDB = TwonlyDB(); twonlyDB = TwonlyDB();

@ -1 +1 @@
Subproject commit 0029f698ea3deb47dedc4ee1f37e5688d1f83f9b Subproject commit e1ac9e6c476f82819063a8087af4d42acc1167d4

View file

@ -62,9 +62,8 @@ Future<bool> initBackgroundExecution() async {
} }
SentryWidgetsFlutterBinding.ensureInitialized(); SentryWidgetsFlutterBinding.ensureInitialized();
globalApplicationCacheDirectory = (await getApplicationCacheDirectory()).path; AppEnvironment.cacheDir = (await getApplicationCacheDirectory()).path;
globalApplicationSupportDirectory = AppEnvironment.supportDir = (await getApplicationSupportDirectory()).path;
(await getApplicationSupportDirectory()).path;
initLogger(); initLogger();

View file

@ -41,9 +41,9 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
Log.info('Starting new twonly Backup!'); Log.info('Starting new twonly Backup!');
final baseDir = globalApplicationSupportDirectory; final backupDir = Directory(
join(AppEnvironment.supportDir, 'backup_twonly_safe/'),
final backupDir = Directory(join(baseDir, 'backup_twonly_safe/')); );
await backupDir.create(recursive: true); await backupDir.create(recursive: true);
final backupDatabaseFile = File(join(backupDir.path, 'twonly.backup.sqlite')); final backupDatabaseFile = File(join(backupDir.path, 'twonly.backup.sqlite'));
@ -53,7 +53,9 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
); );
// copy database // copy database
final originalDatabase = File(join(baseDir, 'twonly.sqlite')); final originalDatabase = File(
join(AppEnvironment.supportDir, 'twonly.sqlite'),
);
await originalDatabase.copy(backupDatabaseFile.path); await originalDatabase.copy(backupDatabaseFile.path);
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;

View file

@ -90,7 +90,7 @@ Future<void> handleBackupData(
); );
final originalDatabase = File( final originalDatabase = File(
join(globalApplicationSupportDirectory, 'twonly.sqlite'), join(AppEnvironment.supportDir, 'twonly.sqlite'),
); );
await originalDatabase.writeAsBytes(backupContent.twonlyDatabase); await originalDatabase.writeAsBytes(backupContent.twonlyDatabase);

View file

@ -27,7 +27,7 @@ class MediaFileService {
try { try {
final tempDirectory = MediaFileService.buildDirectoryPath( final tempDirectory = MediaFileService.buildDirectoryPath(
'tmp', 'tmp',
globalApplicationSupportDirectory, AppEnvironment.supportDir,
); );
final files = tempDirectory.listSync(); final files = tempDirectory.listSync();
@ -307,7 +307,7 @@ class MediaFileService {
} }
final mediaBaseDir = buildDirectoryPath( final mediaBaseDir = buildDirectoryPath(
directory, directory,
globalApplicationSupportDirectory, AppEnvironment.supportDir,
); );
return File( return File(
join(mediaBaseDir.path, '${mediaFile.mediaId}$namePrefix.$extension'), join(mediaBaseDir.path, '${mediaFile.mediaId}$namePrefix.$extension'),

View file

@ -37,7 +37,7 @@ Future<void> createPushAvatars({int? forceForUserId}) async {
File avatarPNGFile(int contactId) { File avatarPNGFile(int contactId) {
final avatarsDirectory = Directory( final avatarsDirectory = Directory(
'$globalApplicationCacheDirectory/avatars', '${AppEnvironment.cacheDir}/avatars',
); );
if (!avatarsDirectory.existsSync()) { if (!avatarsDirectory.existsSync()) {

View file

@ -8,7 +8,7 @@ Future<T> exclusiveAccess<T>({
required Future<T> Function() action, required Future<T> Function() action,
required Mutex mutex, required Mutex mutex,
}) async { }) async {
final lockFile = File('$globalApplicationSupportDirectory/$lockName.lock'); final lockFile = File('${AppEnvironment.supportDir}/$lockName.lock');
return mutex.protect(() async { return mutex.protect(() async {
var lockAcquired = false; var lockAcquired = false;

View file

@ -5,7 +5,7 @@ import 'package:twonly/src/utils/log.dart';
class KeyValueStore { class KeyValueStore {
static Future<File> _getFilePath(String key) async { static Future<File> _getFilePath(String key) async {
return File('$globalApplicationSupportDirectory/keyvalue/$key.json'); return File('${AppEnvironment.supportDir}/keyvalue/$key.json');
} }
static Future<void> delete(String key) async { static Future<void> delete(String key) async {

View file

@ -72,7 +72,7 @@ class Log {
Future<String> loadLogFile() async { Future<String> loadLogFile() async {
return _protectFileAccess(() async { return _protectFileAccess(() async {
final logFile = File('$globalApplicationSupportDirectory/app.log'); final logFile = File('${AppEnvironment.supportDir}/app.log');
if (logFile.existsSync()) { if (logFile.existsSync()) {
return logFile.readAsString(); return logFile.readAsString();
@ -84,7 +84,7 @@ Future<String> loadLogFile() async {
Future<String> readLast1000Lines() async { Future<String> readLast1000Lines() async {
return _protectFileAccess(() async { return _protectFileAccess(() async {
final file = File('$globalApplicationSupportDirectory/app.log'); final file = File('${AppEnvironment.supportDir}/app.log');
if (!file.existsSync()) return ''; if (!file.existsSync()) return '';
final all = await file.readAsLines(); final all = await file.readAsLines();
final start = all.length > 1000 ? all.length - 1000 : 0; final start = all.length > 1000 ? all.length - 1000 : 0;
@ -103,7 +103,7 @@ Future<T> _protectFileAccess<T>(Future<T> Function() action) async {
} }
Future<void> _writeLogToFile(LogRecord record) async { Future<void> _writeLogToFile(LogRecord record) async {
final logFile = File('$globalApplicationSupportDirectory/app.log'); final logFile = File('${AppEnvironment.supportDir}/app.log');
final logMessage = final logMessage =
'${clock.now().toString().split(".")[0]} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n'; '${clock.now().toString().split(".")[0]} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n';
@ -127,7 +127,7 @@ Future<void> _writeLogToFile(LogRecord record) async {
Future<void> cleanLogFile() async { Future<void> cleanLogFile() async {
return _protectFileAccess(() async { return _protectFileAccess(() async {
final logFile = File('$globalApplicationSupportDirectory/app.log'); final logFile = File('${AppEnvironment.supportDir}/app.log');
if (!logFile.existsSync()) { if (!logFile.existsSync()) {
return; return;
@ -162,7 +162,7 @@ Future<void> cleanLogFile() async {
Future<bool> deleteLogFile() async { Future<bool> deleteLogFile() async {
return _protectFileAccess(() async { return _protectFileAccess(() async {
final logFile = File('$globalApplicationSupportDirectory/app.log'); final logFile = File('${AppEnvironment.supportDir}/app.log');
if (logFile.existsSync()) { if (logFile.existsSync()) {
await logFile.delete(); await logFile.delete();

View file

@ -592,7 +592,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (mc.selectedCameraDetails.cameraId >= gCameras.length || if (mc.selectedCameraDetails.cameraId >= AppEnvironment.cameras.length ||
mc.cameraController == null) { mc.cameraController == null) {
return Container(); return Container();
} }

View file

@ -105,16 +105,17 @@ class MainCameraController {
initCameraStarted = true; initCameraStarted = true;
var cameraId = sCameraId; var cameraId = sCameraId;
if (cameraId >= gCameras.length) { if (cameraId >= AppEnvironment.cameras.length) {
Log.warn( Log.warn(
'Trying to select a non existing camera $cameraId >= ${gCameras.length}', 'Trying to select a non existing camera $cameraId >= ${AppEnvironment.cameras.length}',
); );
return; return;
} }
if (init) { if (init) {
for (; cameraId < gCameras.length; cameraId++) { for (; cameraId < AppEnvironment.cameras.length; cameraId++) {
if (gCameras[cameraId].lensDirection == CameraLensDirection.back) { if (AppEnvironment.cameras[cameraId].lensDirection ==
CameraLensDirection.back) {
break; break;
} }
} }
@ -124,7 +125,7 @@ class MainCameraController {
if (cameraController == null) { if (cameraController == null) {
cameraController = CameraController( cameraController = CameraController(
gCameras[cameraId], AppEnvironment.cameras[cameraId],
ResolutionPreset.high, ResolutionPreset.high,
enableAudio: await Permission.microphone.isGranted, enableAudio: await Permission.microphone.isGranted,
imageFormatGroup: Platform.isAndroid imageFormatGroup: Platform.isAndroid
@ -150,7 +151,7 @@ class MainCameraController {
selectedCameraDetails.scaleFactor = 1; selectedCameraDetails.scaleFactor = 1;
await cameraController?.setZoomLevel(1); await cameraController?.setZoomLevel(1);
await cameraController?.setDescription(gCameras[cameraId]); await cameraController?.setDescription(AppEnvironment.cameras[cameraId]);
try { try {
if (!isVideoRecording) { if (!isVideoRecording) {
await cameraController?.startImageStream(_processCameraImage); await cameraController?.startImageStream(_processCameraImage);

View file

@ -51,11 +51,11 @@ class _CameraZoomButtonsState extends State<CameraZoomButtons> {
Future<void> initAsync() async { Future<void> initAsync() async {
showWideAngleZoom = (await widget.controller.getMinZoomLevel()) < 1; showWideAngleZoom = (await widget.controller.getMinZoomLevel()) < 1;
var index = gCameras.indexWhere( var index = AppEnvironment.cameras.indexWhere(
(t) => t.lensType == CameraLensType.ultraWide, (t) => t.lensType == CameraLensType.ultraWide,
); );
if (index == -1) { if (index == -1) {
index = gCameras.indexWhere( index = AppEnvironment.cameras.indexWhere(
(t) => t.lensType == CameraLensType.wide, (t) => t.lensType == CameraLensType.wide,
); );
} }

View file

@ -48,7 +48,7 @@ class _ExportMediaViewState extends State<ExportMediaView> {
Directory _mediaFolder() { Directory _mediaFolder() {
final dir = MediaFileService.buildDirectoryPath( final dir = MediaFileService.buildDirectoryPath(
'stored', 'stored',
globalApplicationSupportDirectory, AppEnvironment.supportDir,
); );
if (!dir.existsSync()) dir.createSync(recursive: true); if (!dir.existsSync()) dir.createSync(recursive: true);
return dir; return dir;