frosty/lib/cache_manager.dart
Tommy Chow 16f2722a5b perf: async startup cache sweep, memoize chat name color
the orphaned-cache-file sweep ran a synchronous recursive listSync on
the UI isolate at startup against a cache that can hold 10k files; use
the async listing instead. memoize adjustChatNameColor keyed by (color,
background, target contrast) since chat repeats the same handful of
palette colors thousands of times and low-contrast ones pay a 16-step
search per call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:45:38 -04:00

47 lines
1.4 KiB
Dart

import 'dart:io';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:path_provider/path_provider.dart';
class CustomCacheManager {
static const key = 'libCachedImageData';
static final _repo = CacheObjectProvider(databaseName: key);
static final instance = CacheManager(
Config(
key,
stalePeriod: const Duration(days: 30),
maxNrOfCacheObjects: 10000,
repo: _repo,
),
);
static Future<void> removeOrphanedCacheFiles() async {
final tempDir = await getTemporaryDirectory();
final cacheDir = Directory('${tempDir.path}/$key');
if (!await cacheDir.exists()) {
return;
}
// Async listing — the cache can hold up to maxNrOfCacheObjects (10k)
// files, and a synchronous recursive listing that large would block the
// UI isolate during startup.
final allFiles = await cacheDir.list(recursive: true).toList();
await _repo.open();
final cachedObjects = await _repo.getAllObjects();
final dbFiles = cachedObjects.map((e) => e.relativePath).toSet();
final orphanedFiles = allFiles.where((file) {
final relativePath = file.path.split('$key/').last;
return !dbFiles.contains(relativePath);
}).toList();
final deletions = orphanedFiles.map((file) => file.delete()).toList();
try {
await Future.wait(deletions);
// ignore: empty_catches
} catch (e) {}
}
}