From 32231d11c208f35d8d39f7d5f313b366d7ce8d7c Mon Sep 17 00:00:00 2001 From: otsmr Date: Sat, 16 May 2026 20:37:34 +0200 Subject: [PATCH] small redesign of the image view --- .../camera_preview_controller_view.dart | 19 +----- .../chat_list_entry.dart | 35 +++++----- .../entries/chat_audio_entry.dart | 25 +------ .../entries/chat_media_entry.dart | 51 ++++++-------- .../entries/chat_text_entry.dart | 28 ++------ .../entries/common.dart | 26 ++++--- .../entries/friendly_message_time.comp.dart | 67 ++++++++++--------- .../in_chat_media_viewer.dart | 42 ++++++++---- .../visual/views/memories/memories.view.dart | 2 +- 9 files changed, 132 insertions(+), 163 deletions(-) diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart index 396e1cdf..2ccec5ac 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart @@ -441,6 +441,9 @@ class _CameraPreviewViewState extends State { await mc.cameraController!.setZoomLevel( mc.selectedCameraDetails.scaleFactor, ); + if (!userService.currentUser.hasZoomed) { + await UserService.update((u) => u.hasZoomed = true); + } } Future pickImageFromGallery() async { @@ -613,12 +616,6 @@ class _CameraPreviewViewState extends State { if (mounted) showSnackbar(context, 'Error: $e'); } - void _incrementZoomUsageCount() { - if (!userService.currentUser.hasZoomed) { - UserService.update((u) => u.hasZoomed = true); - } - } - @override Widget build(BuildContext context) { if (mc.selectedCameraDetails.cameraId >= AppEnvironment.cameras.length || @@ -666,19 +663,9 @@ class _CameraPreviewViewState extends State { }, onLongPressEnd: (a) { stopVideoRecording(); - if ((mc.selectedCameraDetails.scaleFactor - _baseScaleFactor) - .abs() > - 0.05) { - _incrementZoomUsageCount(); - } }, onPanEnd: (a) { stopVideoRecording(); - if ((mc.selectedCameraDetails.scaleFactor - _baseScaleFactor) - .abs() > - 0.05) { - _incrementZoomUsageCount(); - } }, onPanUpdate: onPanUpdate, child: Stack( diff --git a/lib/src/visual/views/chats/chat_messages_components/chat_list_entry.dart b/lib/src/visual/views/chats/chat_messages_components/chat_list_entry.dart index eef515d6..76135e60 100644 --- a/lib/src/visual/views/chats/chat_messages_components/chat_list_entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/chat_list_entry.dart @@ -101,15 +101,16 @@ class _ChatListEntryState extends State { setState(() {}); } - Widget? _getChatEntry(BorderRadius borderRadius, int reactionsForWidth) { + Widget? _getChatEntry( + BorderRadius borderRadius, + int reactionsForWidth, + BubbleInfo info, + ) { if (widget.message.type == MessageType.text.name) { return ChatTextEntry( message: widget.message, - nextMessage: widget.nextMessage, - prevMessage: widget.prevMessage, - userIdToContact: widget.userIdToContact, borderRadius: borderRadius, - minWidth: reactionsForWidth * 43, + info: info, ); } @@ -118,12 +119,9 @@ class _ChatListEntryState extends State { if (mediaService!.mediaFile.type == MediaType.audio) { return ChatAudioEntry( message: widget.message, - nextMessage: widget.nextMessage, - prevMessage: widget.prevMessage, mediaService: mediaService!, - userIdToContact: widget.userIdToContact, borderRadius: borderRadius, - minWidth: reactionsForWidth * 43, + info: info, ); } return ChatMediaEntry( @@ -131,7 +129,8 @@ class _ChatListEntryState extends State { group: widget.group, mediaService: mediaService!, galleryItems: widget.galleryItems, - minWidth: reactionsForWidth * 43, + borderRadius: borderRadius, + info: info, ); } @@ -168,6 +167,15 @@ class _ChatListEntryState extends State { .length; if (reactionsForWidth > 4) reactionsForWidth = 4; + final info = getBubbleInfo( + context, + widget.message, + widget.nextMessage, + widget.prevMessage, + widget.userIdToContact, + reactionsForWidth * 43.0, + ); + Widget child = Stack( // overflow: Overflow.visible, // clipBehavior: Clip.none, @@ -176,11 +184,8 @@ class _ChatListEntryState extends State { if (widget.message.isDeletedFromSender) ChatTextEntry( message: widget.message, - nextMessage: widget.nextMessage, - prevMessage: widget.prevMessage, - userIdToContact: widget.userIdToContact, borderRadius: borderRadius, - minWidth: reactionsForWidth * 43, + info: info, ) else Column( @@ -191,7 +196,7 @@ class _ChatListEntryState extends State { mediaService: mediaService, borderRadius: borderRadius, scrollToMessage: widget.scrollToMessage, - child: _getChatEntry(borderRadius, reactionsForWidth), + child: _getChatEntry(borderRadius, reactionsForWidth, info), ), if (reactionsForWidth > 0) const SizedBox(height: 20, width: 10), ], diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart index 3d3adf89..349ace0f 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart @@ -13,22 +13,16 @@ import 'package:twonly/src/visual/views/chats/chat_messages_components/message_s class ChatAudioEntry extends StatelessWidget { const ChatAudioEntry({ required this.message, - required this.nextMessage, required this.mediaService, - required this.prevMessage, required this.borderRadius, - required this.userIdToContact, - required this.minWidth, + required this.info, super.key, }); final Message message; final MediaFileService mediaService; - final Message? nextMessage; - final Message? prevMessage; - final Map? userIdToContact; final BorderRadius borderRadius; - final double minWidth; + final BubbleInfo info; @override Widget build(BuildContext context) { @@ -36,14 +30,6 @@ class ChatAudioEntry extends StatelessWidget { !mediaService.originalPath.existsSync()) { return Container(); // media file was purged } - final info = getBubbleInfo( - context, - message, - nextMessage, - prevMessage, - userIdToContact, - minWidth, - ); return LayoutBuilder( builder: (context, constraints) { @@ -63,12 +49,7 @@ class ChatAudioEntry extends StatelessWidget { maxWidth: MediaQuery.of(context).size.width * 0.8, minWidth: 250, ), - padding: const EdgeInsets.only( - left: 10, - top: 6, - bottom: 6, - right: 10, - ), + padding: info.padding, decoration: BoxDecoration( color: info.color, borderRadius: borderRadius, diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart index 96c7607c..28b5c949 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart @@ -26,15 +26,17 @@ class ChatMediaEntry extends StatefulWidget { required this.group, required this.galleryItems, required this.mediaService, - required this.minWidth, + required this.borderRadius, + required this.info, super.key, }); final Message message; - final double minWidth; final Group group; final List galleryItems; final MediaFileService mediaService; + final BorderRadius borderRadius; + final BubbleInfo info; @override State createState() => _ChatMediaEntryState(); @@ -116,52 +118,34 @@ class _ChatMediaEntryState extends State { context, ); - var imageBorderRadius = BorderRadius.circular(12); + var imageBorderRadius = widget.borderRadius; Widget additionalMessageData = Container(); final addData = widget.message.additionalMessageData; if (addData != null) { - final info = getBubbleInfo( - context, - widget.message, - null, - null, - null, - 200, - ); final data = AdditionalMessageData.fromBuffer(addData); if (data.hasLink() && widget.message.mediaStored) { - imageBorderRadius = const BorderRadius.only( - topLeft: Radius.circular(12), - topRight: Radius.circular(12), - bottomLeft: Radius.circular(5), - bottomRight: Radius.circular(5), + imageBorderRadius = widget.borderRadius.copyWith( + bottomLeft: const Radius.circular(5), + bottomRight: const Radius.circular(5), ); additionalMessageData = Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * 0.8, ), - padding: const EdgeInsets.only( - left: 10, - top: 6, - bottom: 6, - right: 10, - ), + padding: widget.info.padding, decoration: BoxDecoration( - color: info.color, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(12), - bottomLeft: Radius.circular(12), - bottomRight: Radius.circular(12), + color: widget.info.color, + borderRadius: widget.borderRadius.copyWith( + topLeft: const Radius.circular(5), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - BetterText(text: data.link, textColor: info.textColor), + BetterText(text: data.link, textColor: widget.info.textColor), ], ), ); @@ -178,7 +162,12 @@ class _ChatMediaEntryState extends State { onDoubleTap: onDoubleTap, onTap: (widget.message.type == MessageType.media.name) ? onTap : null, child: SizedBox( - width: (widget.minWidth > 150) ? widget.minWidth : 150, + width: (widget.info.minWidth > 150) + ? widget.info.minWidth + : (widget.message.mediaStored && + widget.mediaService.imagePreviewAvailable) + ? 150 + : null, height: (widget.message.mediaStored && widget.mediaService.imagePreviewAvailable) @@ -195,6 +184,8 @@ class _ChatMediaEntryState extends State { color: color, galleryItems: widget.galleryItems, canBeReopened: _canBeReopened, + borderRadius: imageBorderRadius, + info: widget.info, ), ), ), diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_text_entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_text_entry.dart index fd1339be..67d0a1a5 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_text_entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_text_entry.dart @@ -8,20 +8,14 @@ import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/f class ChatTextEntry extends StatelessWidget { const ChatTextEntry({ required this.message, - required this.nextMessage, - required this.prevMessage, required this.borderRadius, - required this.userIdToContact, - required this.minWidth, + required this.info, super.key, }); final Message message; - final Message? nextMessage; - final Message? prevMessage; - final Map? userIdToContact; final BorderRadius borderRadius; - final double minWidth; + final BubbleInfo info; @override Widget build(BuildContext context) { @@ -40,15 +34,6 @@ class ChatTextEntry extends StatelessWidget { ); } - final info = getBubbleInfo( - context, - message, - nextMessage, - prevMessage, - userIdToContact, - minWidth, - ); - return LayoutBuilder( builder: (context, constraints) { final textWidth = measureTextWidth(info.text); @@ -65,14 +50,9 @@ class ChatTextEntry extends StatelessWidget { return Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * 0.8, - minWidth: minWidth, - ), - padding: const EdgeInsets.only( - left: 10, - top: 6, - bottom: 6, - right: 10, + minWidth: info.minWidth, ), + padding: info.padding, decoration: BoxDecoration( color: info.color, borderRadius: borderRadius, diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/common.dart b/lib/src/visual/views/chats/chat_messages_components/entries/common.dart index 06f8a928..84eb3e99 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/common.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/common.dart @@ -14,6 +14,8 @@ class BubbleInfo { late Color color; late bool expanded; late double spacerWidth; + late EdgeInsets padding; + late double minWidth; } BubbleInfo getBubbleInfo( @@ -29,7 +31,11 @@ BubbleInfo getBubbleInfo( ..textColor = Colors.white ..color = getMessageColor(message.senderId != null) ..displayTime = !combineTextMessageWithNext(message, nextMessage) - ..displayUserName = ''; + ..displayUserName = '' + ..minWidth = minWidth + ..padding = message.type == MessageType.media.name + ? const EdgeInsets.symmetric(horizontal: 10, vertical: 2) + : const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10); if (message.senderId != null && userIdToContact != null && @@ -86,17 +92,15 @@ double measureTextWidth( } bool combineTextMessageWithNext(Message message, Message? nextMessage) { - if (nextMessage != null && nextMessage.content != null) { + if (nextMessage != null) { if (nextMessage.senderId == message.senderId) { - if (nextMessage.type == MessageType.text.name && - message.type == MessageType.text.name) { - if (!EmojiAnimationComp.supported(nextMessage.content!)) { - final diff = nextMessage.createdAt - .difference(message.createdAt) - .inMinutes; - if (diff <= 1) { - return true; - } + if (nextMessage.content == null || + !EmojiAnimationComp.supported(nextMessage.content!)) { + final diff = nextMessage.createdAt + .difference(message.createdAt) + .inMinutes; + if (diff <= 1) { + return true; } } } diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart b/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart index ef5ea52c..3fdf953b 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart @@ -6,46 +6,49 @@ import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; class FriendlyMessageTime extends StatelessWidget { - const FriendlyMessageTime({required this.message, super.key}); + const FriendlyMessageTime({ + required this.message, + this.color, + super.key, + }); final Message message; + final Color? color; @override Widget build(BuildContext context) { - return Align( - alignment: AlignmentGeometry.centerRight, - child: Padding( - padding: const EdgeInsets.only(left: 6), - child: Row( - children: [ - if (message.modifiedAt != null && !message.isDeletedFromSender) - Padding( - padding: const EdgeInsets.only(right: 5), - child: SizedBox( - height: 10, - child: FaIcon( - FontAwesomeIcons.pencil, - color: Colors.white.withAlpha(150), - size: 10, - ), + return Padding( + padding: const EdgeInsets.only(left: 6), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (message.modifiedAt != null && !message.isDeletedFromSender) + Padding( + padding: const EdgeInsets.only(right: 5), + child: SizedBox( + height: 10, + child: FaIcon( + FontAwesomeIcons.pencil, + color: color ?? Colors.white.withAlpha(150), + size: 10, ), ), - Text( - friendlyTime( - context, - (message.modifiedAt != null) - ? message.modifiedAt! - : message.createdAt, - ), - style: TextStyle( - fontSize: 10, - color: Colors.white.withAlpha(150), - decoration: TextDecoration.none, - fontWeight: FontWeight.normal, - ), ), - ], - ), + Text( + friendlyTime( + context, + (message.modifiedAt != null) + ? message.modifiedAt! + : message.createdAt, + ), + style: TextStyle( + fontSize: 10, + color: color ?? Colors.white.withAlpha(150), + decoration: TextDecoration.none, + fontWeight: FontWeight.normal, + ), + ), + ], ), ); } diff --git a/lib/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart b/lib/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart index 038ccc31..2770fa6e 100644 --- a/lib/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart +++ b/lib/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart @@ -5,6 +5,9 @@ import 'package:twonly/locator.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/model/memory_item.model.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/common.dart'; +import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/message_send_state_icon.dart'; import 'package:twonly/src/visual/views/memories/components/memory_thumbnail.comp.dart'; import 'package:twonly/src/visual/views/memories/synchronized_viewer.view.dart'; @@ -17,6 +20,8 @@ class InChatMediaViewer extends StatefulWidget { required this.color, required this.galleryItems, required this.canBeReopened, + required this.borderRadius, + required this.info, super.key, }); @@ -26,6 +31,8 @@ class InChatMediaViewer extends StatefulWidget { final List galleryItems; final Color color; final bool canBeReopened; + final BorderRadius borderRadius; + final BubbleInfo info; @override State createState() => _InChatMediaViewerState(); @@ -147,21 +154,32 @@ class _InChatMediaViewerState extends State { minHeight: 39, ), decoration: BoxDecoration( + color: widget.info.color.withValues(alpha: 0.3), border: Border.all( - color: widget.color, + color: widget.info.color.withValues(alpha: 0.4), ), - borderRadius: BorderRadius.circular(12), + borderRadius: widget.borderRadius, ), child: Padding( - padding: EdgeInsets.symmetric( - vertical: (widget.canBeReopened) ? 5 : 10.0, - horizontal: 4, - ), - child: MessageSendStateIcon( - [widget.message], - [widget.mediaService.mediaFile], - mainAxisAlignment: MainAxisAlignment.center, - canBeReopened: widget.canBeReopened, + padding: widget.info.padding, + child: Row( + children: [ + MessageSendStateIcon( + [widget.message], + [widget.mediaService.mediaFile], + mainAxisAlignment: widget.message.senderId == null + ? MainAxisAlignment.end + : MainAxisAlignment.start, + canBeReopened: widget.canBeReopened, + ), + if (widget.info.displayTime || widget.message.modifiedAt != null) + FriendlyMessageTime( + message: widget.message, + color: isDarkMode(context) + ? Colors.white.withAlpha(100) + : Colors.black.withAlpha(100), + ), + ], ), ), ); @@ -172,7 +190,7 @@ class _InChatMediaViewerState extends State { color: Colors.transparent, ), color: Colors.transparent, - borderRadius: BorderRadius.circular(12), + borderRadius: widget.borderRadius, ), child: galleryItemIndex != null ? MemoriesThumbnailComp( diff --git a/lib/src/visual/views/memories/memories.view.dart b/lib/src/visual/views/memories/memories.view.dart index 43da6b84..1a19c44b 100644 --- a/lib/src/visual/views/memories/memories.view.dart +++ b/lib/src/visual/views/memories/memories.view.dart @@ -411,7 +411,7 @@ class MemoriesViewState extends State { strokeWidth: 2.5, color: context.color.primary, backgroundColor: context.color.primary - .withOpacity(0.2), + .withValues(alpha: 0.2), ), ), ),