Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TW-180: Display preview received image #185

Merged
merged 6 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions lib/pages/chat/chat_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,7 @@ class ChatView extends StatelessWidget {
onTapDown: controller.setReadMarker,
behavior: HitTestBehavior.opaque,
child: StreamBuilder(
stream: controller.room!.onUpdate.stream
.rateLimit(const Duration(seconds: 1)),
stream: controller.room!.onUpdate.stream.rateLimit(const Duration(seconds: 1)),
builder: (context, snapshot) => FutureBuilder<bool>(
future: controller.getTimeline(),
builder: (BuildContext context, snapshot) {
Expand Down
84 changes: 34 additions & 50 deletions lib/pages/chat/events/image_bubble.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import 'package:flutter/material.dart';

import 'package:flutter_blurhash/flutter_blurhash.dart';
import 'package:matrix/matrix.dart';

import 'package:fluffychat/pages/image_viewer/image_viewer.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/mxc_image.dart';

class ImageBubble extends StatelessWidget {
Expand All @@ -17,7 +13,8 @@ class ImageBubble extends StatelessWidget {
final bool animated;
final double width;
final double height;
final void Function()? onTap;
final void Function()? onTapPreview;
final void Function()? onTapSelectMode;

const ImageBubble(
this.event, {
Expand All @@ -29,7 +26,8 @@ class ImageBubble extends StatelessWidget {
this.width = 256,
this.height = 300,
this.animated = false,
this.onTap,
this.onTapSelectMode,
this.onTapPreview,
Key? key,
}) : super(key: key);

Expand All @@ -40,12 +38,12 @@ class ImageBubble extends StatelessWidget {
);
}
final String blurHashString =
event.infoMap['xyz.amorgan.blurhash'] is String
? event.infoMap['xyz.amorgan.blurhash']
: 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';
event.infoMap['xyz.amorgan.blurhash'] is String
? event.infoMap['xyz.amorgan.blurhash']
: 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';
final ratio = event.infoMap['w'] is int && event.infoMap['h'] is int
? event.infoMap['w'] / event.infoMap['h']
: 1.0;
? event.infoMap['w'] / event.infoMap['h']
: 1.0;
var width = 32;
var height = 32;
if (ratio > 1.0) {
Expand All @@ -65,47 +63,33 @@ class ImageBubble extends StatelessWidget {
);
}

void _onTap(BuildContext context) {
if (onTap != null) {
onTap!();
return;
}
if (!tapToView) return;
showDialog(
context: Matrix.of(context).navigatorContext,
useRootNavigator: false,
builder: (_) => ImageViewer(event),
);
}

@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => _onTap(context),
child: Hero(
tag: event.eventId,
child: AnimatedSwitcher(
duration: const Duration(seconds: 1),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20)
),
constraints: maxSize
? BoxConstraints(
maxWidth: width,
maxHeight: height,
)
: null,
child: MxcImage(
rounded: true,
event: event,
width: width,
height: height,
fit: fit,
animated: animated,
isThumbnail: thumbnailOnly,
placeholder: _buildPlaceholder,
),
return Hero(
tag: event.eventId,
child: AnimatedSwitcher(
duration: const Duration(seconds: 1),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20)
),
constraints: maxSize
? BoxConstraints(
maxWidth: width,
maxHeight: height,
)
: null,
child: MxcImage(
rounded: true,
event: event,
width: width,
height: height,
fit: fit,
animated: animated,
isThumbnail: thumbnailOnly,
placeholder: _buildPlaceholder,
onTapPreview: onTapPreview,
onTapSelectMode: onTapSelectMode,
),
),
),
Expand Down
3 changes: 2 additions & 1 deletion lib/pages/chat/events/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ class Message extends StatelessWidget {
backgroundColor: ownMessage
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surface,
onTapImage: () => controller.selectMode ? onSelect!(event) : null,
onTapSelectMode: () => controller.selectMode ? onSelect!(event) : null,
onTapPreview: !controller.selectMode ? () {} : null,
),
if (timelineOverlayMessage)
Positioned(
Expand Down
11 changes: 7 additions & 4 deletions lib/pages/chat/events/message_content.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/events/message_content_style.dart';
import 'package:fluffychat/pages/chat/events/sending_image_widget.dart';
import 'package:fluffychat/widgets/twake_link_text.dart';
Expand Down Expand Up @@ -32,7 +31,8 @@ class MessageContent extends StatelessWidget {
final void Function(Event)? onInfoTab;
final Widget endOfBubbleWidget;
final Color backgroundColor;
final Function()? onTapImage;
final Function()? onTapPreview;
final Function()? onTapSelectMode;

const MessageContent(
this.event, {
Expand All @@ -41,7 +41,8 @@ class MessageContent extends StatelessWidget {
required this.textColor,
required this.endOfBubbleWidget,
required this.backgroundColor,
this.onTapImage,
this.onTapPreview,
this.onTapSelectMode
}) : super(key: key);

void _verifyOrRequestKey(BuildContext context) async {
Expand Down Expand Up @@ -132,14 +133,16 @@ class MessageContent extends StatelessWidget {
return SendingImageWidget(
sendingImageData: sendingImageData,
event: event,
onTapPreview: onTapPreview,
);
}
return ImageBubble(
event,
width: MessageContentStyle.imageBubbleWidth,
height: MessageContentStyle.imageBubbleHeight,
fit: BoxFit.cover,
onTap: onTapImage,
onTapSelectMode: onTapSelectMode,
onTapPreview: onTapPreview,
);
case MessageTypes.Sticker:
if (event.redacted) continue textmessage;
Expand Down
40 changes: 30 additions & 10 deletions lib/pages/chat/events/sending_image_widget.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:typed_data';

import 'package:fluffychat/pages/chat/events/message_content_style.dart';
import 'package:fluffychat/pages/image_viewer/image_viewer.dart';
import 'package:flutter/material.dart';
import 'package:linagora_design_flutter/colors/linagora_ref_colors.dart';
import 'package:matrix/matrix.dart' hide Visibility;
Expand All @@ -9,14 +10,30 @@ class SendingImageWidget extends StatelessWidget {
SendingImageWidget({
super.key,
required this.sendingImageData,
required this.event,
required this.event, this.onTapPreview,
});

final Uint8List sendingImageData;

final Event event;

final ValueNotifier<double> sendingFileProgressNotifier = ValueNotifier(0);
final void Function()? onTapPreview;

final ValueNotifier<double> sendingFileProgressNotifier = ValueNotifier(0);

void _onTap(BuildContext context) async {
if (onTapPreview != null) {
await showGeneralDialog(
context: context,
useRootNavigator: false,
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (_, animationOne, animationTwo) =>
ImageViewer(event, imageData: sendingImageData)
);
}
}

@override
Widget build(BuildContext context) {
Expand All @@ -42,14 +59,17 @@ class SendingImageWidget extends StatelessWidget {
],
);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(12.0),
child: Image.memory(
sendingImageData,
width: MessageContentStyle.imageBubbleWidth,
height: MessageContentStyle.imageBubbleHeight,
fit: BoxFit.cover,
filterQuality: FilterQuality.medium,
child: InkWell(
onTap: () => _onTap(context),
child: ClipRRect(
borderRadius: BorderRadius.circular(12.0),
child: Image.memory(
sendingImageData,
width: MessageContentStyle.imageBubbleWidth,
height: MessageContentStyle.imageBubbleHeight,
fit: BoxFit.cover,
filterQuality: FilterQuality.medium,
),
),
),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/pages/chat/events/sticker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class StickerState extends State<Sticker> {
width: 400,
height: 400,
fit: BoxFit.contain,
onTap: () {
onTapPreview: () {
setState(() => animated = true);
showOkAlertDialog(
context: context,
Expand Down
2 changes: 1 addition & 1 deletion lib/pages/forward/forward_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class ForwardItem extends StatelessWidget {
],
),
),
const SizedBox(width: 4),
const SizedBox(width: 8),
Flexible(
child: Text(
displayName,
Expand Down
28 changes: 20 additions & 8 deletions lib/pages/image_viewer/image_viewer.dart
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
import 'dart:typed_data';
import 'package:fluffychat/pages/forward/forward.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:flutter/material.dart';

import 'package:matrix/matrix.dart';
import 'package:vrouter/vrouter.dart';

import 'package:fluffychat/pages/image_viewer/image_viewer_view.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../utils/matrix_sdk_extensions/event_extension.dart';

class ImageViewer extends StatefulWidget {
final Event event;
final Uint8List? imageData;

const ImageViewer(this.event, {Key? key}) : super(key: key);
const ImageViewer(this.event, {Key? key, this.imageData}) : super(key: key);

@override
ImageViewerController createState() => ImageViewerController();
}

class ImageViewerController extends State<ImageViewer> {

final ValueNotifier<bool> showAppbarPreview = ValueNotifier(false);

/// Forward this image to another room.
void forwardAction() {
void forwardAction() async {
Matrix.of(context).shareContent = widget.event.content;
VRouter.of(context).to('/rooms');
await showDialog(
context: context,
useSafeArea: false,
useRootNavigator: false,
builder: (c) => const Forward(),
);
}

void toggleAppbarPreview() {
showAppbarPreview.value = !showAppbarPreview.value;
}

/// Save this file with a system call.
Expand All @@ -43,5 +55,5 @@ class ImageViewerController extends State<ImageViewer> {
}

@override
Widget build(BuildContext context) => ImageViewerView(this);
Widget build(BuildContext context) => ImageViewerView(this, imageData: widget.imageData);
}
Loading
Loading