Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .config
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions .config.dev
Original file line number Diff line number Diff line change
@@ -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
9 changes: 1 addition & 8 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,9 @@ jobs:
- name: Clone repository
uses: actions/checkout@v4

- name: Create .env files
- name: Add google services
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
Expand Down
77 changes: 71 additions & 6 deletions lib/data/repository/image_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,38 @@ class ImageRepository extends CacheImpl<ImageEntity> implements IImageRepository
@override
final ImageType type;

@override
Future<void> 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 getAll();

// 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<String?> _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';
}

Expand Down Expand Up @@ -103,10 +132,7 @@ class ImageRepository extends CacheImpl<ImageEntity> 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) {
Expand Down Expand Up @@ -162,7 +188,46 @@ class ImageRepository extends CacheImpl<ImageEntity> implements IImageRepository

@override
Future<void> 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();
});
}

@override
Future<void> 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++;
}
}
}
}

Expand Down
5 changes: 2 additions & 3 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final sharedPreferences = await SharedPreferences.getInstance();
Expand All @@ -45,9 +44,9 @@ Future<void> 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;

Expand Down
9 changes: 4 additions & 5 deletions lib/util/core/cache_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ abstract class CacheImpl<T extends CacheEntity> implements CacheApi<T> {


CacheImpl({required this.box, required this.isar, this.maxItems, this.ttlDuration}) {
_startup();
startup();
}

@protected
Expand Down Expand Up @@ -55,7 +55,7 @@ abstract class CacheImpl<T extends CacheEntity> implements CacheApi<T> {

@override
Future<List<T>> getAll() async {
return box.where().findAll();
return await box.where().findAll();
}

@override
Expand All @@ -78,7 +78,8 @@ abstract class CacheImpl<T extends CacheEntity> implements CacheApi<T> {
return await box.getAll(ids.map(fastHash).toList());
}

void _startup() {
@protected
void startup() {
DateTime? ttlTime;
if (ttlDuration != null) {
ttlTime = DateTime.now().subtract(ttlDuration!);
Expand All @@ -93,8 +94,6 @@ abstract class CacheImpl<T extends CacheEntity> implements CacheApi<T> {

}

/// Delete the items with the lowest hit count
/// Not included are items with keepAlive == true and items younger than 10% of ttlDuration
@override
Future<void> deleteOldestItems() async {

Expand Down
1 change: 0 additions & 1 deletion lib/util/routing/routing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
6 changes: 3 additions & 3 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -94,8 +94,8 @@ flutter:
- assets/image/
- assets/achievements/
- assets/icon/
- .env
- .env.dev
- .config
- .config.dev
uses-material-design: true
fonts:
- family: Signika
Expand Down
Loading