From d3c59e08630147bc04ffb0897b0b8cbf73ce7b9c Mon Sep 17 00:00:00 2001 From: Lukas Reim Date: Mon, 9 Mar 2026 21:33:47 +0100 Subject: [PATCH 1/4] Add startup overwrite for image repo --- lib/data/repository/image_repository.dart | 48 ++++++++++++++++++++--- lib/util/core/cache_impl.dart | 5 ++- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/lib/data/repository/image_repository.dart b/lib/data/repository/image_repository.dart index 3921c175..3d5c9c09 100644 --- a/lib/data/repository/image_repository.dart +++ b/lib/data/repository/image_repository.dart @@ -45,6 +45,36 @@ class ImageRepository extends CacheImpl implements IImageRepository @override final ImageType type; + @override + Future startup() async { + DateTime? ttlTime; + if (ttlDuration != null) { + ttlTime = DateTime.now().subtract(ttlDuration!); + } + + await isar.writeTxn(() async { + // 1. Fetch only items matching this repository's Type + final all = await box.filter().typeEqualTo(type).findAll(); + + // 2. Filter by session-only or expired TTL + final toDelete = all.where((entry) => + (entry.onlySession && !entry.keepAlive) || + (ttlTime != null && !entry.keepAlive && entry.ttl.isBefore(ttlTime)) + ).toList(); + + // 3. Delete physical files first + for (final entry in toDelete) { + if (entry.filePath.isNotEmpty) { + final file = File(entry.filePath); + if (await file.exists()) { + await file.delete(); + } + } + await box.delete(entry.isarId); + } + }); + } + Future _getImagePath(String id) async { final directory = await getApplicationDocumentsDirectory(); // Add type.name to path to avoid file name collisions @@ -103,10 +133,7 @@ class ImageRepository extends CacheImpl implements IImageRepository final filePath = await _getImagePath(id); if (filePath != null) { await File(filePath).writeAsBytes(image.bodyBytes); - if (keepAlive) { - // 3. Include type in all entity creations - await put(ImageEntity(id: id, type: type, filePath: filePath, keepAlive: keepAlive, ttl: DateTime.now(), onlySession: false)); - } + await put(ImageEntity(id: id, type: type, filePath: filePath, keepAlive: keepAlive, ttl: DateTime.now(), onlySession: false)); } return image.bodyBytes; } catch (e) { @@ -162,7 +189,18 @@ class ImageRepository extends CacheImpl implements IImageRepository @override Future deleteAll() async { - await isar.writeTxn(() async => await box.filter().typeEqualTo(type).deleteAll()); + await isar.writeTxn(() async { + final items = await box.filter().typeEqualTo(type).findAll(); + for (final item in items) { + if (item.filePath.isNotEmpty) { + final file = File(item.filePath); + if (await file.exists()) { + await file.delete(); + } + } + } + await box.filter().typeEqualTo(type).deleteAll(); + }); } } diff --git a/lib/util/core/cache_impl.dart b/lib/util/core/cache_impl.dart index 2fa552dc..5c974e44 100644 --- a/lib/util/core/cache_impl.dart +++ b/lib/util/core/cache_impl.dart @@ -13,7 +13,7 @@ abstract class CacheImpl implements CacheApi { CacheImpl({required this.box, required this.isar, this.maxItems, this.ttlDuration}) { - _startup(); + startup(); } @protected @@ -78,7 +78,8 @@ abstract class CacheImpl implements CacheApi { return await box.getAll(ids.map(fastHash).toList()); } - void _startup() { + @protected + void startup() { DateTime? ttlTime; if (ttlDuration != null) { ttlTime = DateTime.now().subtract(ttlDuration!); From 8373f3c6673e5aa17c20d8ea573e417b71562971 Mon Sep 17 00:00:00 2001 From: Lukas Reim Date: Mon, 9 Mar 2026 21:34:09 +0100 Subject: [PATCH 2/4] Bump version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6d91580c..ff0ac597 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A new Flutter project. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 3.0.18+64 +version: 3.0.19+64 environment: From 0d4ffcbc72d285e93929434d770b0107f4e942d9 Mon Sep 17 00:00:00 2001 From: Lukas Reim Date: Tue, 10 Mar 2026 21:29:41 +0100 Subject: [PATCH 3/4] Fix image repo setup fuction --- .config | 5 ++++ .config.dev | 6 +++++ .github/workflows/main.yml | 8 ------ lib/data/repository/image_repository.dart | 31 +++++++++++++++++++++-- lib/main.dart | 5 ++-- lib/util/core/cache_impl.dart | 4 +-- lib/util/routing/routing.dart | 1 - pubspec.yaml | 4 +-- 8 files changed, 45 insertions(+), 19 deletions(-) create mode 100644 .config create mode 100644 .config.dev diff --git a/.config b/.config new file mode 100644 index 00000000..8bf7dd12 --- /dev/null +++ b/.config @@ -0,0 +1,5 @@ +APPSTORE_ID=6443754033 +URL_GITHUB_REPO=https://github.com/lr101/stick-it +INSTAGRAM_URL=https://www.instagram.com/stick_it_app +DISCORD_INVITE=https://discord.gg/ReMZ8j6S8X +API_HOST=https://stick-it.lr-projects.de \ No newline at end of file diff --git a/.config.dev b/.config.dev new file mode 100644 index 00000000..4b064c6a --- /dev/null +++ b/.config.dev @@ -0,0 +1,6 @@ +APPSTORE_ID=6443754033 +GITHUB_URL=https://github.com/lr101/stick-it +INSTAGRAM_URL=https://www.instagram.com/stick_it_app +DISCORD_INVITE=https://discord.gg/ReMZ8j6S8X +MINIO_HOST=https://minio.lr-projects.de/stick-it-prod +API_HOST=https://stick-it.lr-projects.de \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index de986ce0..0eb1dba7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,16 +15,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Create .env files run: | - touch .env - touch .env.dev touch ./android/app/google-services.json - echo "API_HOST=${{ secrets.API_HOST }}" > .env - echo "DISCORD_INVITE=${{ secrets.DISCORD_INVITE }}" >> .env - echo "INSTAGRAM_URL=${{ secrets.INSTAGRAM_URL }}" >> .env - echo "URL_GITHUB_REPO=${{ secrets.URL_GITHUB_REPO }}" >> .env - echo "APPSTORE_ID=${{ secrets.APPSTORE_ID }}" >> .env echo "${{ secrets.GOOGLE_SERVICES }}" | base64 -d > ./android/app/google-services.json - name: Set up Flutter diff --git a/lib/data/repository/image_repository.dart b/lib/data/repository/image_repository.dart index 3d5c9c09..4cc1b2f1 100644 --- a/lib/data/repository/image_repository.dart +++ b/lib/data/repository/image_repository.dart @@ -54,7 +54,7 @@ class ImageRepository extends CacheImpl implements IImageRepository await isar.writeTxn(() async { // 1. Fetch only items matching this repository's Type - final all = await box.filter().typeEqualTo(type).findAll(); + final all = await getAll(); // 2. Filter by session-only or expired TTL final toDelete = all.where((entry) => @@ -77,7 +77,6 @@ class ImageRepository extends CacheImpl implements IImageRepository Future _getImagePath(String id) async { final directory = await getApplicationDocumentsDirectory(); - // Add type.name to path to avoid file name collisions return '${directory.path}/${urlSubFolder}_${type.name}_${id}_$urlFileName'; } @@ -202,6 +201,34 @@ class ImageRepository extends CacheImpl implements IImageRepository await box.filter().typeEqualTo(type).deleteAll(); }); } + + @override + Future deleteOldestItems() async { + + final size = await isar.getSize(); + if (maxItems == null || maxItems! >= size) return; + + + final entries = await box.where().findAll(); + + entries.sort((a, b) { + final aHits = a.hits; + final bHits = b.hits; + return aHits.compareTo(bHits); + }); + + final itemsToDelete = size - maxItems!; + int itemsDeleted = 0; + final duration = ttlDuration != null ? (ttlDuration!.inSeconds * 0.1).toInt() : 3600; + final ttlTime = DateTime.now().subtract(Duration(seconds: duration)); + + for (int i = 0; i < entries.length && itemsDeleted < itemsToDelete; i++) { + if (entries[i].keepAlive == false && entries[i].ttl.isBefore(ttlTime)) { + await box.delete(entries[i].isarId); + itemsDeleted++; + } + } + } } diff --git a/lib/main.dart b/lib/main.dart index 6fe48007..91e91f07 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -35,7 +35,6 @@ import 'package:shared_preferences/shared_preferences.dart'; /// THIS IS THE START OF THE PROGRAMM /// binding Widgets before initialization is required by multiple packages /// initializes access to env variables -/// checks if user is logged in on this device by checking device storage Future main() async { WidgetsFlutterBinding.ensureInitialized(); final sharedPreferences = await SharedPreferences.getInstance(); @@ -45,9 +44,9 @@ Future main() async { const bool isProduction = bool.fromEnvironment('dart.vm.product'); if (isProduction) { - await dotenv.load(); + await dotenv.load(fileName: ".config"); } else { - await dotenv.load(fileName: ".env.dev"); + await dotenv.load(fileName: ".config.dev"); } Isar? isar; diff --git a/lib/util/core/cache_impl.dart b/lib/util/core/cache_impl.dart index 5c974e44..0f05c447 100644 --- a/lib/util/core/cache_impl.dart +++ b/lib/util/core/cache_impl.dart @@ -55,7 +55,7 @@ abstract class CacheImpl implements CacheApi { @override Future> getAll() async { - return box.where().findAll(); + return await box.where().findAll(); } @override @@ -94,8 +94,6 @@ abstract class CacheImpl implements CacheApi { } - /// Delete the items with the lowest hit count - /// Not included are items with keepAlive == true and items younger than 10% of ttlDuration @override Future deleteOldestItems() async { diff --git a/lib/util/routing/routing.dart b/lib/util/routing/routing.dart index 567a5f2c..0a2d3fdf 100644 --- a/lib/util/routing/routing.dart +++ b/lib/util/routing/routing.dart @@ -24,7 +24,6 @@ import 'package:buff_lisa/features/settings/presentation/sub_widgets/edit_hidden import 'package:buff_lisa/features/web/presentation/show_web.dart'; import 'package:buff_lisa/widgets/custom_interaction/presentation/custom_error_snack_bar.dart'; import 'package:buff_lisa/widgets/report_issue/presentation/report_issue_page.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:latlong2/latlong.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index ff0ac597..7e09d458 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -94,8 +94,8 @@ flutter: - assets/image/ - assets/achievements/ - assets/icon/ - - .env - - .env.dev + - .config + - .config.dev uses-material-design: true fonts: - family: Signika From b127985f3c7d30115b17873511b8284340e9f698 Mon Sep 17 00:00:00 2001 From: Lukas Reim Date: Tue, 10 Mar 2026 21:33:32 +0100 Subject: [PATCH 4/4] Fix workflow --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0eb1dba7..a59e7fc0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,7 @@ jobs: - name: Clone repository uses: actions/checkout@v4 + - name: Add google services run: | touch ./android/app/google-services.json echo "${{ secrets.GOOGLE_SERVICES }}" | base64 -d > ./android/app/google-services.json