remove unused animations and start pre-caching later

This commit is contained in:
otsmr 2026-06-20 00:01:14 +02:00
parent 19ffed3386
commit c4fc14a909
3 changed files with 101 additions and 141 deletions

View file

@ -662,6 +662,25 @@ class _MediaViewerViewState extends State<MediaViewerView> {
); );
} }
void _sendTextMessage() {
if (textMessageController.text.isNotEmpty) {
unawaited(
insertAndSendTextMessage(
widget.group.groupId,
textMessageController.text,
currentMessage!.messageId,
),
);
textMessageController.clear();
}
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
showSendTextMessageInput = false;
showShortReactions = false;
_lastTimeInputClosed = clock.now();
});
}
void onScreenTapped() { void onScreenTapped() {
if (_lastTimeInputClosed != null && if (_lastTimeInputClosed != null &&
clock.now().difference(_lastTimeInputClosed!) < clock.now().difference(_lastTimeInputClosed!) <
@ -774,30 +793,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
if (showSendTextMessageInput) if (showSendTextMessageInput)
MediaViewerMessageInput( MediaViewerMessageInput(
controller: textMessageController, controller: textMessageController,
onSubmitted: (value) { onSubmitted: (value) => _sendTextMessage(),
setState(() { onSendPressed: _sendTextMessage,
showSendTextMessageInput = false;
showShortReactions = false;
_lastTimeInputClosed = clock.now();
});
},
onSendPressed: () {
if (textMessageController.text.isNotEmpty) {
unawaited(
insertAndSendTextMessage(
widget.group.groupId,
textMessageController.text,
currentMessage!.messageId,
),
);
textMessageController.clear();
}
setState(() {
showSendTextMessageInput = false;
showShortReactions = false;
_lastTimeInputClosed = clock.now();
});
},
), ),
if (currentMessage != null) if (currentMessage != null)
AdditionalMessageContent(currentMessage!), AdditionalMessageContent(currentMessage!),

View file

@ -39,6 +39,7 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
double _offsetFromOne = 0; double _offsetFromOne = 0;
bool _isBottomNavVisible = true; bool _isBottomNavVisible = true;
Timer? _disableCameraTimer; Timer? _disableCameraTimer;
bool _startPreloading = false;
final MainCameraController _mainCameraController = MainCameraController(); final MainCameraController _mainCameraController = MainCameraController();
late final PageController _homeViewPageController; late final PageController _homeViewPageController;
@ -138,6 +139,13 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
widget.initialPage == 0) { widget.initialPage == 0) {
streamHomeViewPageIndex.add(0); streamHomeViewPageIndex.add(0);
} }
Future.delayed(const Duration(seconds: 1), () {
if (mounted) {
setState(() {
_startPreloading = true;
});
}
});
}); });
} }
@ -319,7 +327,9 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const PageScrollPhysics(), physics: const PageScrollPhysics(),
controller: _homeViewPageController, controller: _homeViewPageController,
scrollCacheExtent: const ScrollCacheExtent.viewport(1), scrollCacheExtent: _startPreloading
? const ScrollCacheExtent.viewport(1)
: null,
slivers: [ slivers: [
SliverFillViewport( SliverFillViewport(
delegate: SliverChildListDelegate([ delegate: SliverChildListDelegate([

View file

@ -4,6 +4,7 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/model/memory_item.model.dart'; import 'package:twonly/src/model/memory_item.model.dart';
import 'package:twonly/src/visual/components/selectable_thumbnail.comp.dart'; import 'package:twonly/src/visual/components/selectable_thumbnail.comp.dart';
import 'package:twonly/src/visual/views/memories/components/memory_transition_painter.dart'; import 'package:twonly/src/visual/views/memories/components/memory_transition_painter.dart';
class MemoriesThumbnailComp extends StatefulWidget { class MemoriesThumbnailComp extends StatefulWidget {
const MemoriesThumbnailComp({ const MemoriesThumbnailComp({
required this.galleryItem, required this.galleryItem,
@ -28,14 +29,7 @@ class MemoriesThumbnailComp extends StatefulWidget {
State<MemoriesThumbnailComp> createState() => _MemoriesThumbnailCompState(); State<MemoriesThumbnailComp> createState() => _MemoriesThumbnailCompState();
} }
final Set<String> _alreadyAnimatedIds = {}; class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp> {
class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp>
with SingleTickerProviderStateMixin {
late final AnimationController _scaleController;
late final Animation<double> _scaleAnimation;
late final Animation<Offset> _slideAnimation;
ImageProvider? _imageProvider; ImageProvider? _imageProvider;
ImageStream? _imageStream; ImageStream? _imageStream;
ImageInfo? _imageInfo; ImageInfo? _imageInfo;
@ -44,40 +38,6 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_scaleController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 350),
);
_scaleAnimation = Tween<double>(begin: 0.94, end: 1).animate(
CurvedAnimation(parent: _scaleController, curve: Curves.easeOutCubic),
);
_slideAnimation =
Tween<Offset>(
begin: const Offset(0, 0.125),
end: Offset.zero,
).animate(
CurvedAnimation(parent: _scaleController, curve: Curves.easeOutCubic),
);
final mediaId = widget.galleryItem.mediaService.mediaFile.mediaId;
final shouldAnimate =
widget.index < 20 && !_alreadyAnimatedIds.contains(mediaId);
if (shouldAnimate) {
_alreadyAnimatedIds.add(mediaId);
final delayMs = widget.index * 10;
if (delayMs > 0) {
Future.delayed(Duration(milliseconds: delayMs), () {
if (mounted) {
_scaleController.forward();
}
});
} else {
_scaleController.forward();
}
} else {
_scaleController.value = 1.0;
}
_listener = ImageStreamListener( _listener = ImageStreamListener(
(info, _) { (info, _) {
@ -101,8 +61,11 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp>
void _resolveImage() { void _resolveImage() {
final media = widget.galleryItem.mediaService; final media = widget.galleryItem.mediaService;
final hasThumbnail = media.thumbnailPath.existsSync() && media.thumbnailPath.lengthSync() > 0; final hasThumbnail =
final hasStored = media.storedPath.existsSync() && media.storedPath.lengthSync() > 0; media.thumbnailPath.existsSync() &&
media.thumbnailPath.lengthSync() > 0;
final hasStored =
media.storedPath.existsSync() && media.storedPath.lengthSync() > 0;
final isImageOrGif = final isImageOrGif =
media.mediaFile.type == MediaType.image || media.mediaFile.type == MediaType.image ||
media.mediaFile.type == MediaType.gif; media.mediaFile.type == MediaType.gif;
@ -136,7 +99,6 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp>
@override @override
void dispose() { void dispose() {
_scaleController.dispose();
_imageStream?.removeListener(_listener); _imageStream?.removeListener(_listener);
super.dispose(); super.dispose();
} }
@ -169,80 +131,71 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp>
); );
} }
: null, : null,
child: SlideTransition( child: SelectableThumbnailComp(
position: _slideAnimation, isSelected: widget.isSelected,
child: ScaleTransition( selectionMode: widget.selectionMode,
scale: _scaleAnimation, child: Stack(
child: FadeTransition( fit: StackFit.expand,
opacity: _scaleController, children: [
child: SelectableThumbnailComp( if (cachedInfo != null)
isSelected: widget.isSelected, RawImage(
selectionMode: widget.selectionMode, image: cachedInfo.image,
child: Stack( fit: BoxFit.cover,
fit: StackFit.expand, )
children: [ else if (_imageProvider != null)
if (cachedInfo != null) Image(
RawImage( image: _imageProvider!,
image: cachedInfo.image, fit: BoxFit.cover,
fit: BoxFit.cover, gaplessPlayback: true,
) errorBuilder: (context, error, stackTrace) {
else if (_imageProvider != null) return ColoredBox(
Image( color: Colors.grey.shade200,
image: _imageProvider!, child: const Center(
fit: BoxFit.cover, child: FaIcon(
gaplessPlayback: true, FontAwesomeIcons.image,
errorBuilder: (context, error, stackTrace) { color: Colors.black26,
return ColoredBox(
color: Colors.grey.shade200,
child: const Center(
child: FaIcon(
FontAwesomeIcons.image,
color: Colors.black26,
),
),
);
},
)
else
ColoredBox(
color: Colors.grey.shade200,
child: const Center(
child: FaIcon(
FontAwesomeIcons.image,
color: Colors.black26,
),
), ),
), ),
if (isVideo) );
const Positioned.fill( },
child: Center( )
child: FaIcon( else
FontAwesomeIcons.circlePlay, ColoredBox(
color: Colors.white, color: Colors.grey.shade200,
size: 32, child: const Center(
shadows: [ child: FaIcon(
Shadow(color: Colors.black54, blurRadius: 6), FontAwesomeIcons.image,
], color: Colors.black26,
), ),
), ),
),
if (media.mediaFile.isFavorite)
const Positioned(
bottom: 6,
left: 6,
child: Icon(
Icons.favorite,
color: Colors.redAccent,
size: 16,
shadows: [
Shadow(color: Colors.black54, blurRadius: 4),
],
),
),
],
), ),
), if (isVideo)
), const Positioned.fill(
child: Center(
child: FaIcon(
FontAwesomeIcons.circlePlay,
color: Colors.white,
size: 32,
shadows: [
Shadow(color: Colors.black54, blurRadius: 6),
],
),
),
),
if (media.mediaFile.isFavorite)
const Positioned(
bottom: 6,
left: 6,
child: Icon(
Icons.favorite,
color: Colors.redAccent,
size: 16,
shadows: [
Shadow(color: Colors.black54, blurRadius: 4),
],
),
),
],
), ),
), ),
); );