diff --git a/lib/components/base/text.dart b/lib/components/base/text.dart index 2deb6df..0ef3113 100644 --- a/lib/components/base/text.dart +++ b/lib/components/base/text.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:moodiary/components/base/marquee.dart'; +import 'package:moodiary/utils/lru.dart'; class AdaptiveText extends StatelessWidget { final String text; @@ -82,6 +83,8 @@ class EllipsisText extends StatelessWidget { final String ellipsis; final int? maxLines; + static final _cache = LRUCache(maxSize: 1000); + const EllipsisText( this.text, { super.key, @@ -90,10 +93,7 @@ class EllipsisText extends StatelessWidget { this.maxLines, }); - double _calculateTextWidth( - String text, - TextScaler textScaler, - ) { + double _calculateTextWidth(String text, TextScaler textScaler) { final span = TextSpan(text: text.fixAutoLines(), style: style); final tp = TextPainter( text: span, @@ -120,15 +120,31 @@ class EllipsisText extends StatelessWidget { @override Widget build(BuildContext context) { final textScaler = MediaQuery.textScalerOf(context); + return LayoutBuilder(builder: (context, constraints) { if (text.isEmpty) { return const SizedBox.shrink(); } + + final cacheKey = + '${text.hashCode}_${ellipsis}_${constraints.maxWidth}_$maxLines'; + final cachedResult = _cache.get(cacheKey); + if (cachedResult != null) { + return Text( + cachedResult, + maxLines: maxLines, + overflow: TextOverflow.ellipsis, + style: style, + textScaler: textScaler, + ); + } + if (!_createTextPainter( text, constraints.maxWidth, textScaler, ).didExceedMaxLines) { + _cache.put(cacheKey, text.fixAutoLines()); return Text( text.fixAutoLines(), maxLines: maxLines, @@ -143,8 +159,6 @@ class EllipsisText extends StatelessWidget { double leftWidth = 0; double rightWidth = 0; - String truncatedText = text; - int lastValidLeftIndex = 0; int lastValidRightIndex = text.characters.length; @@ -157,6 +171,7 @@ class EllipsisText extends StatelessWidget { rightWidth; final currentText = '${text.runeSubstring(0, leftIndex)}$ellipsis${text.runeSubstring(rightIndex)}'; + if (_createTextPainter( currentText, constraints.maxWidth, @@ -176,10 +191,10 @@ class EllipsisText extends StatelessWidget { } } - final leftText = text.runeSubstring(0, lastValidLeftIndex); - final rightText = text.runeSubstring(lastValidRightIndex); + final truncatedText = + '${text.runeSubstring(0, lastValidLeftIndex)}$ellipsis${text.runeSubstring(lastValidRightIndex)}'; - truncatedText = '$leftText$ellipsis$rightText'; + _cache.put(cacheKey, truncatedText.fixAutoLines()); return Text( truncatedText.fixAutoLines(), diff --git a/lib/utils/lru.dart b/lib/utils/lru.dart new file mode 100644 index 0000000..804950f --- /dev/null +++ b/lib/utils/lru.dart @@ -0,0 +1,72 @@ +import 'dart:collection'; + +import 'package:synchronized/synchronized.dart'; + +class LRUCache { + final int maxSize; + final LinkedHashMap _map; + + LRUCache({this.maxSize = 1000}) : _map = LinkedHashMap(); + + V? get(K key) { + if (!_map.containsKey(key)) return null; + final value = _map.remove(key) as V; + _map[key] = value; + return value; + } + + void put(K key, V value) { + if (_map.containsKey(key)) { + _map.remove(key); + } else if (_map.length >= maxSize) { + _map.remove(_map.keys.first); + } + _map[key] = value; + } + + void clear() => _map.clear(); + + int size() => _map.length; + + @override + String toString() => _map.toString(); +} + +class AsyncLRUCache { + final int maxSize; + final LinkedHashMap _map = LinkedHashMap(); + final Lock _lock = Lock(); + + AsyncLRUCache({this.maxSize = 1000}); + + Future get(K key) async { + return await _lock.synchronized(() async { + if (!_map.containsKey(key)) return null; + final value = _map.remove(key) as V; + _map[key] = value; + return value; + }); + } + + Future put(K key, V value) async { + await _lock.synchronized(() async { + if (_map.containsKey(key)) { + _map.remove(key); + } else if (_map.length >= maxSize) { + _map.remove(_map.keys.first); + } + _map[key] = value; + }); + } + + Future clear() async { + await _lock.synchronized(() => _map.clear()); + } + + Future size() async { + return await _lock.synchronized(() => _map.length); + } + + @override + String toString() => _map.toString(); +} diff --git a/lib/utils/media_util.dart b/lib/utils/media_util.dart index 5ff3e2f..0751455 100644 --- a/lib/utils/media_util.dart +++ b/lib/utils/media_util.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:collection'; import 'dart:io'; import 'package:fc_native_video_thumbnail/fc_native_video_thumbnail.dart'; @@ -16,6 +15,7 @@ import 'package:moodiary/src/rust/api/compress.dart'; import 'package:moodiary/src/rust/api/constants.dart' as r_type; import 'package:moodiary/utils/file_util.dart'; import 'package:moodiary/utils/log_util.dart'; +import 'package:moodiary/utils/lru.dart'; import 'package:moodiary/utils/notice_util.dart'; import 'package:path/path.dart'; import 'package:uuid/uuid.dart'; @@ -25,6 +25,9 @@ class MediaUtil { static final _thumbnail = FcNativeVideoThumbnail(); + static final _imageAspectRatioCache = + AsyncLRUCache(maxSize: 1000); + static void useAndroidImagePicker() { final ImagePickerPlatform imagePickerImplementation = ImagePickerPlatform.instance; @@ -186,36 +189,25 @@ class MediaUtil { return completer.future; } - static const int _maxCacheSize = 1000; - static final LinkedHashMap _cache = LinkedHashMap(); - static Future getImageAspectRatio(ImageProvider imageProvider) async { final key = _getImageKey(imageProvider); - - if (_cache.containsKey(key)) { - return _cache[key]!; + final cachedAspectRatio = await _imageAspectRatioCache.get(key); + if (cachedAspectRatio != null) { + return cachedAspectRatio; } final completer = Completer(); final imageStream = imageProvider.resolve(const ImageConfiguration()); imageStream.addListener( - ImageStreamListener((ImageInfo info, bool _) { + ImageStreamListener((ImageInfo info, bool _) async { final aspectRatio = info.image.width / info.image.height; - _addToCache(key, aspectRatio); + await _imageAspectRatioCache.put(key, aspectRatio); completer.complete(aspectRatio); }), ); - return completer.future; } - static void _addToCache(String key, double ratio) { - if (_cache.length >= _maxCacheSize) { - _cache.remove(_cache.keys.first); - } - _cache[key] = ratio; - } - static String _getImageKey(ImageProvider imageProvider) { if (imageProvider is AssetImage) { return 'asset_${imageProvider.assetName}'; diff --git a/pubspec.lock b/pubspec.lock index 9b9a619..9b5fb28 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -478,14 +478,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" - csv: - dependency: transitive - description: - name: csv - sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c - url: "https://pub.dev" - source: hosted - version: "6.0.0" cupertino_icons: dependency: "direct main" description: @@ -1330,14 +1322,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" - json2yaml: - dependency: transitive - description: - name: json2yaml - sha256: da94630fbc56079426fdd167ae58373286f603371075b69bf46d848d63ba3e51 - url: "https://pub.dev" - source: hosted - version: "3.0.1" json_annotation: dependency: transitive description: @@ -1709,18 +1693,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: b15fad91c4d3d1f2b48c053dd41cb82da007c27407dc9ab5f9aa59881d0e39d4 + sha256: c447a3c3e7be4addf129b8f9ab6a4bd5d166b78918223e223b61fddf4d07e254 url: "https://pub.dev" source: hosted - version: "8.1.4" + version: "8.2.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: a5ef9986efc7bf772f2696183a3992615baa76c1ffb1189318dd8803778fb05b + sha256: "205ec83335c2ab9107bbba3f8997f9356d72ca3c715d2f038fc773d0366b4c76" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.1.0" path: dependency: "direct main" description: @@ -2186,22 +2170,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.8" - sensors_plus: - dependency: "direct main" - description: - name: sensors_plus - sha256: "905282c917c6bb731c242f928665c2ea15445aa491249dea9d98d7c79dc8fd39" - url: "https://pub.dev" - source: hosted - version: "6.1.1" - sensors_plus_platform_interface: - dependency: transitive - description: - name: sensors_plus_platform_interface - sha256: "58815d2f5e46c0c41c40fb39375d3f127306f7742efe3b891c0b1c87e2b5cd5d" - url: "https://pub.dev" - source: hosted - version: "2.0.1" share_plus: dependency: "direct main" description: @@ -2311,30 +2279,6 @@ packages: description: flutter source: sdk version: "0.0.0" - slang: - dependency: "direct main" - description: - name: slang - sha256: "2778b88f05ffc23fd0a37436f607bf2a541d0b6b922e69a8ea5bbd50c2427d18" - url: "https://pub.dev" - source: hosted - version: "4.4.1" - slang_build_runner: - dependency: "direct dev" - description: - name: slang_build_runner - sha256: "15b251e8aa591d96a16da3678abdca814a5d21486d14f98602c52b169f794d7b" - url: "https://pub.dev" - source: hosted - version: "4.4.2" - slang_flutter: - dependency: "direct main" - description: - name: slang_flutter - sha256: "819637a23348adbc4f4e8faee3f274d8908f9af31d57bf1e277cd730b14bacde" - url: "https://pub.dev" - source: hosted - version: "4.4.0" sliver_tools: dependency: "direct main" description: @@ -2488,7 +2432,7 @@ packages: source: hosted version: "28.2.3" synchronized: - dependency: transitive + dependency: "direct main" description: name: synchronized sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" diff --git a/pubspec.yaml b/pubspec.yaml index 8892d79..31744f6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,7 +27,7 @@ dependencies: image_picker: 1.1.2 device_info_plus: 11.2.2 photo_view: 0.15.0 - package_info_plus: 8.1.4 + package_info_plus: 8.2.0 uuid: 4.5.1 flutter_quill: 11.0.0-dev.21 share_plus: 10.1.4 @@ -101,16 +101,14 @@ dependencies: sdk: flutter moodiary_rust: path: rust_builder - slang: 4.4.1 - slang_flutter: 4.4.0 modal_bottom_sheet: 3.0.0 sheet: 1.0.0 tutorial_coach_mark: 1.2.12 adaptive_dialog: 2.4.0 flutter_inappwebview: 6.1.5 - sensors_plus: 6.1.1 markdown: 7.3.0 flutter_highlight: 0.7.0 + synchronized: 3.3.0+3 # //llama_cpp_dart: 0.0.8 @@ -119,7 +117,6 @@ dev_dependencies: # sdk: flutter build_runner: 2.4.14 flutter_launcher_icons: 0.14.3 - slang_build_runner: 4.4.2 msix: 3.16.8 flutter_lints: 5.0.0