small redesign of the image view
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run

This commit is contained in:
otsmr 2026-05-16 20:37:34 +02:00
parent 68c99c271f
commit 32231d11c2
9 changed files with 132 additions and 163 deletions

View file

@ -441,6 +441,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
await mc.cameraController!.setZoomLevel(
mc.selectedCameraDetails.scaleFactor,
);
if (!userService.currentUser.hasZoomed) {
await UserService.update((u) => u.hasZoomed = true);
}
}
Future<void> pickImageFromGallery() async {
@ -613,12 +616,6 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
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<CameraPreviewView> {
},
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(

View file

@ -101,15 +101,16 @@ class _ChatListEntryState extends State<ChatListEntry> {
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<ChatListEntry> {
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<ChatListEntry> {
group: widget.group,
mediaService: mediaService!,
galleryItems: widget.galleryItems,
minWidth: reactionsForWidth * 43,
borderRadius: borderRadius,
info: info,
);
}
@ -168,6 +167,15 @@ class _ChatListEntryState extends State<ChatListEntry> {
.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<ChatListEntry> {
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<ChatListEntry> {
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),
],

View file

@ -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<int, Contact>? 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,

View file

@ -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<MemoryItem> galleryItems;
final MediaFileService mediaService;
final BorderRadius borderRadius;
final BubbleInfo info;
@override
State<ChatMediaEntry> createState() => _ChatMediaEntryState();
@ -116,52 +118,34 @@ class _ChatMediaEntryState extends State<ChatMediaEntry> {
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<ChatMediaEntry> {
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<ChatMediaEntry> {
color: color,
galleryItems: widget.galleryItems,
canBeReopened: _canBeReopened,
borderRadius: imageBorderRadius,
info: widget.info,
),
),
),

View file

@ -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<int, Contact>? 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,

View file

@ -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,11 +92,10 @@ 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!)) {
if (nextMessage.content == null ||
!EmojiAnimationComp.supported(nextMessage.content!)) {
final diff = nextMessage.createdAt
.difference(message.createdAt)
.inMinutes;
@ -100,6 +105,5 @@ bool combineTextMessageWithNext(Message message, Message? nextMessage) {
}
}
}
}
return false;
}

View file

@ -6,17 +6,21 @@ 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(
return Padding(
padding: const EdgeInsets.only(left: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (message.modifiedAt != null && !message.isDeletedFromSender)
Padding(
@ -25,7 +29,7 @@ class FriendlyMessageTime extends StatelessWidget {
height: 10,
child: FaIcon(
FontAwesomeIcons.pencil,
color: Colors.white.withAlpha(150),
color: color ?? Colors.white.withAlpha(150),
size: 10,
),
),
@ -39,14 +43,13 @@ class FriendlyMessageTime extends StatelessWidget {
),
style: TextStyle(
fontSize: 10,
color: Colors.white.withAlpha(150),
color: color ?? Colors.white.withAlpha(150),
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
],
),
),
);
}
}

View file

@ -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<MemoryItem> galleryItems;
final Color color;
final bool canBeReopened;
final BorderRadius borderRadius;
final BubbleInfo info;
@override
State<InChatMediaViewer> createState() => _InChatMediaViewerState();
@ -147,22 +154,33 @@ class _InChatMediaViewerState extends State<InChatMediaViewer> {
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(
padding: widget.info.padding,
child: Row(
children: [
MessageSendStateIcon(
[widget.message],
[widget.mediaService.mediaFile],
mainAxisAlignment: MainAxisAlignment.center,
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<InChatMediaViewer> {
color: Colors.transparent,
),
color: Colors.transparent,
borderRadius: BorderRadius.circular(12),
borderRadius: widget.borderRadius,
),
child: galleryItemIndex != null
? MemoriesThumbnailComp(

View file

@ -411,7 +411,7 @@ class MemoriesViewState extends State<MemoriesView> {
strokeWidth: 2.5,
color: context.color.primary,
backgroundColor: context.color.primary
.withOpacity(0.2),
.withValues(alpha: 0.2),
),
),
),