moxxy/lib/ui/widgets/chat/media/media.dart

199 lines
6.4 KiB
Dart

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:moxxyv2/shared/helpers.dart';
import 'package:moxxyv2/shared/models/media.dart';
import 'package:moxxyv2/shared/models/message.dart';
import 'package:moxxyv2/ui/service/thumbnail.dart';
import 'package:moxxyv2/ui/widgets/chat/media/file.dart';
import 'package:moxxyv2/ui/widgets/chat/media/image.dart';
import 'package:moxxyv2/ui/widgets/chat/media/video.dart';
import 'package:moxxyv2/ui/widgets/chat/playbutton.dart';
import 'package:moxxyv2/ui/widgets/chat/quote/base.dart';
import 'package:moxxyv2/ui/widgets/chat/shared/file.dart';
import 'package:moxxyv2/ui/widgets/chat/shared/image.dart';
import 'package:moxxyv2/ui/widgets/chat/shared/video.dart';
import 'package:moxxyv2/ui/widgets/chat/text.dart';
import 'package:open_file/open_file.dart';
enum MessageType {
text,
image,
video,
// audio
file
}
/// Deduce the type of message we are dealing with to pick the correct
/// widget.
MessageType getMessageType(Message message) {
if (message.isMedia) {
final mime = message.mediaType;
if (mime == null) return MessageType.file;
if (mime.startsWith('image/')) {
return MessageType.image;
} else if (mime.startsWith('video/')) {
return MessageType.video;
}
// TODO(Unknown): Implement audio
//else if (mime.startswith("audio/")) return MessageType.audio;
return MessageType.file;
}
return MessageType.text;
}
/// Build an inlinable message widget
Widget buildMessageWidget(Message message, double maxWidth, BorderRadius radius, bool sent) {
switch (getMessageType(message)) {
case MessageType.text: {
return TextChatWidget(
message,
sent,
topWidget: message.quotes != null ? buildQuoteMessageWidget(message.quotes!, sent) : null,
);
}
case MessageType.image: {
return ImageChatWidget(message, radius, maxWidth, sent);
}
case MessageType.video: {
return VideoChatWidget(message, radius, maxWidth, sent);
}
// TODO(Unknown): Implement audio
//case MessageType.audio: return buildImageMessageWidget(message);
case MessageType.file: {
return FileChatWidget(message, radius, sent);
}
}
}
/// Build a widget that represents a quoted message within another bubble.
Widget buildQuoteMessageWidget(Message message, bool sent, { void Function()? resetQuote}) {
switch (getMessageType(message)) {
case MessageType.text:
return QuoteBaseWidget(message, Text(message.body), sent, resetQuotedMessage: resetQuote);
case MessageType.image:
return QuoteBaseWidget(
message,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: 48,
height: 48,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Image.file(File(message.mediaUrl!)),
),
),
],
),
sent,
resetQuotedMessage: resetQuote,
);
case MessageType.video:
return QuoteBaseWidget(
message,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: 48,
height: 48,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Stack(
alignment: Alignment.center,
children: [
FutureBuilder<Uint8List>(
future: GetIt.I.get<ThumbnailCacheService>().getVideoThumbnail(message.mediaUrl!),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
return Image.memory(
snapshot.data!,
fit: BoxFit.cover,
);
} else {
return const Padding(
padding: EdgeInsets.all(32),
child: Icon(
Icons.error_outline,
size: 32,
),
);
}
} else {
return const CircularProgressIndicator();
}
},
),
const PlayButton(size: 16)
],
),
),
)
],
),
sent,
resetQuotedMessage: resetQuote,
);
// TODO(Unknown): Implement audio
//case MessageType.audio: return const SizedBox();
case MessageType.file:
return QuoteBaseWidget(
message,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 8),
child: Text(filenameFromUrl(message.srcUrl!)),
),
SizedBox(
width: 48,
height: 48,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Stack(
alignment: Alignment.center,
children: const [
ColoredBox(
color: Colors.white60,
),
Icon(
Icons.file_present,
),
],
),
),
)
],
),
sent,
resetQuotedMessage: resetQuote,
);
}
}
Widget buildSharedMediaWidget(SharedMedium medium, String conversationJid) {
if (medium.mime == null) {
return SharedFileWidget(medium.path);
} else if (medium.mime!.startsWith('image/')) {
return SharedImageWidget(medium.path, () => OpenFile.open(medium.path));
} else if (medium.mime!.startsWith('video/')) {
return SharedVideoWidget(
medium.path,
() => OpenFile.open(medium.path),
child: const PlayButton(),
);
}
// TODO(Unknown): Audio
//if (message.mime!.startsWith("audio/")) return const SizedBox();
return SharedFileWidget(medium.path);
}