preview attachments where possible

Fixes #166
This commit is contained in:
Henry Hiles 2026-11-11 15:40:01 -05:00
commit 4448f183ac
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
3 changed files with 99 additions and 68 deletions

View file

@ -33,6 +33,8 @@ import "package:nexus/controllers/power_level.dart";
import "package:nexus/controllers/image_packs.dart"; import "package:nexus/controllers/image_packs.dart";
import "package:nexus/models/content/image_pack.dart"; import "package:nexus/models/content/image_pack.dart";
import "package:nexus/models/content/message.dart"; import "package:nexus/models/content/message.dart";
import "package:nexus/widgets/renderers/message_media.dart";
import "package:nexus/helpers/extensions/is_media.dart";
import "package:nexus/models/content/poll_start.dart"; import "package:nexus/models/content/poll_start.dart";
import "package:nexus/models/image_pack.dart"; import "package:nexus/models/image_pack.dart";
import "package:nexus/widgets/emoji/mxc_emoji.dart"; import "package:nexus/widgets/emoji/mxc_emoji.dart";
@ -482,19 +484,47 @@ class const Composer(
if (attachment != null) if (attachment != null)
Card( Card(
margin: .only(bottom: 8), margin: .only(bottom: 8),
child: ListTile( child: Column(
leading: attachment.$2 == null children: [
? SizedBox( ListTile(
leading: switch (attachment.$2) {
null => SizedBox(
width: 16, width: 16,
height: 16, height: 16,
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
) ),
: Icon(Icons.file_copy), ImageMessageContent() => Icon(Icons.image),
title: Text(attachment.$1), VideoMessageContent() => Icon(Icons.movie),
trailing: IconButton( AudioMessageContent() => Icon(Icons.audiotrack),
onPressed: () => ref.invalidate(attachmentProvider), _ => Icon(Icons.file_copy),
icon: Icon(Icons.close), },
), title: Text(attachment.$1),
trailing: IconButton(
onPressed: () => ref.invalidate(attachmentProvider),
icon: Icon(Icons.close),
),
),
AnimatedSize(
duration: Durations.short4,
alignment: .topCenter,
child: switch (attachment.$2) {
final content?
when content is! FileMessageContent &&
content.isMedia =>
Padding(
padding: EdgeInsets.all(12).copyWith(top: 0),
child: Align(
alignment: .centerLeft,
child: ConstrainedBox(
constraints: .loose(.square(300)),
child: MessageMedia(content),
),
),
),
_ => SizedBox(width: .infinity),
},
),
],
), ),
), ),
AnimatedSize( AnimatedSize(

View file

@ -12,7 +12,6 @@ import "package:nexus/models/location.dart";
import "package:nexus/models/content/poll_start.dart"; import "package:nexus/models/content/poll_start.dart";
import "package:nexus/models/content/sticker.dart"; import "package:nexus/models/content/sticker.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
import "package:nexus/widgets/renderers/file_card.dart";
import "package:nexus/widgets/html/html.dart"; import "package:nexus/widgets/html/html.dart";
import "package:nexus/widgets/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/lazy_loading/message_avatar.dart";
import "package:nexus/widgets/lazy_loading/message_displayname.dart"; import "package:nexus/widgets/lazy_loading/message_displayname.dart";
@ -24,8 +23,8 @@ import "package:nexus/widgets/reactions/reaction_row.dart";
import "package:nexus/widgets/common/timestamp.dart"; import "package:nexus/widgets/common/timestamp.dart";
import "package:nexus/widgets/renderers/url_preview.dart"; import "package:nexus/widgets/renderers/url_preview.dart";
import "package:nexus/widgets/renderers/event_preview.dart"; import "package:nexus/widgets/renderers/event_preview.dart";
import "package:nexus/widgets/players/video.dart"; import "package:nexus/widgets/renderers/message_media.dart";
import "package:nexus/widgets/players/audio.dart"; import "package:nexus/helpers/extensions/is_media.dart";
import "package:nexus/widgets/renderers/poll.dart"; import "package:nexus/widgets/renderers/poll.dart";
import "package:nexus/widgets/threads/thread_summary.dart"; import "package:nexus/widgets/threads/thread_summary.dart";
@ -125,63 +124,10 @@ class const MessageRenderer(
), ),
if (!textOnly) ...[ if (!textOnly) ...[
if (event.content if (content.isMedia)
case ImageMessageContent(:final url) ||
FileMessageContent(:final url) ||
VideoMessageContent(:final url) ||
AudioMessageContent(:final url))
ConstrainedBox( ConstrainedBox(
constraints: .loose(.square(500)), constraints: .loose(.square(500)),
child: switch (event.content) { child: MessageMedia(content),
VideoMessageContent(
:final info,
:final file,
:final filename,
:final body,
) =>
VideoPlayer(
url,
info,
encrypted: file != null,
filename: filename ?? body,
),
AudioMessageContent(
:final info,
:final file,
:final filename,
:final body,
) =>
AudioPlayer(
url,
info,
encrypted: file != null,
filename: filename ?? body,
),
FileMessageContent(
:final info,
:final filename,
:final file,
) =>
FileCard(
url,
info,
filename: filename,
encrypted: file != null,
),
ImageMessageContent(
:final info,
:final file,
:final filename,
:final body,
) =>
MessageImage(
url,
info: info,
encrypted: file != null,
filename: filename ?? body,
),
_ => SizedBox.shrink(),
},
), ),
if (event.lastEditRowId != 0) if (event.lastEditRowId != 0)

View file

@ -0,0 +1,55 @@
import "package:material_ui/material_ui.dart";
import "package:nexus/models/content/message.dart";
import "package:nexus/widgets/players/audio.dart";
import "package:nexus/widgets/players/video.dart";
import "package:nexus/widgets/renderers/file_card.dart";
import "package:nexus/widgets/renderers/message_image.dart";
final class const MessageMedia(final MessageContent content, {super.key})
extends StatelessWidget {
@override
Widget build(BuildContext context) => switch (content) {
VideoMessageContent(
:final url,
:final info,
:final file,
:final filename,
:final body,
) =>
VideoPlayer(
url,
info,
encrypted: file != null,
filename: filename ?? body,
),
AudioMessageContent(
:final url,
:final info,
:final file,
:final filename,
:final body,
) =>
AudioPlayer(
url,
info,
encrypted: file != null,
filename: filename ?? body,
),
FileMessageContent(:final url, :final info, :final filename, :final file) =>
FileCard(url, info, filename: filename, encrypted: file != null),
ImageMessageContent(
:final url,
:final info,
:final file,
:final filename,
:final body,
) =>
MessageImage(
url,
info: info,
encrypted: file != null,
filename: filename ?? body,
),
_ => SizedBox.shrink(),
};
}