From 594ac39e3d2ba8848345d4b1e75a3f5b773284f3 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 3 Jul 2025 01:18:07 +0800 Subject: [PATCH] :sparkles: Article attachments --- lib/widgets/content/markdown.dart | 35 +++++++++++++++++++++++++--- lib/widgets/post/compose_shared.dart | 3 +-- lib/widgets/post/post_item.dart | 6 ++++- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/lib/widgets/content/markdown.dart b/lib/widgets/content/markdown.dart index a7243be..86f7c22 100644 --- a/lib/widgets/content/markdown.dart +++ b/lib/widgets/content/markdown.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -6,11 +7,14 @@ import 'package:flutter_highlight/themes/a11y-dark.dart'; import 'package:flutter_highlight/themes/a11y-light.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/models/file.dart'; import 'package:island/pods/config.dart'; import 'package:island/widgets/alert.dart'; +import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/markdown_latex.dart'; import 'package:markdown/markdown.dart' as markdown; import 'package:markdown_widget/markdown_widget.dart'; +import 'package:styled_widget/styled_widget.dart'; import 'package:url_launcher/url_launcher.dart'; import 'image.dart'; @@ -23,6 +27,7 @@ class MarkdownTextContent extends HookConsumerWidget { final TextStyle? linkStyle; final EdgeInsets? linesMargin; final bool isSelectable; + final List? attachments; const MarkdownTextContent({ super.key, @@ -33,6 +38,7 @@ class MarkdownTextContent extends HookConsumerWidget { this.linkStyle, this.isSelectable = false, this.linesMargin, + this.attachments, }); @override @@ -109,6 +115,29 @@ class MarkdownTextContent extends HookConsumerWidget { final uri = Uri.parse(url); if (uri.scheme == 'solian') { switch (uri.host) { + case 'files': + final file = attachments?.firstWhereOrNull( + (file) => file.id == uri.pathSegments[0], + ); + if (file == null) { + return const SizedBox.shrink(); + } + + return ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(8)), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainer, + borderRadius: const BorderRadius.all( + Radius.circular(8), + ), + ), + child: CloudFileWidget( + item: file, + fit: BoxFit.cover, + ).clipRRect(all: 8), + ), + ); case 'stickers': final size = doesEnlargeSticker ? 96.0 : 24.0; return ClipRRect( @@ -132,9 +161,9 @@ class MarkdownTextContent extends HookConsumerWidget { ); } } - final content = UniversalImage( - uri: uri.toString(), - fit: BoxFit.cover, + final content = ConstrainedBox( + constraints: BoxConstraints(maxHeight: 360), + child: UniversalImage(uri: uri.toString(), fit: BoxFit.contain), ); return content; }, diff --git a/lib/widgets/post/compose_shared.dart b/lib/widgets/post/compose_shared.dart index 38cf05c..e8b5e94 100644 --- a/lib/widgets/post/compose_shared.dart +++ b/lib/widgets/post/compose_shared.dart @@ -480,8 +480,7 @@ class ComposeLogic { return; } final cloudFile = attachment.data as SnCloudFile; - final baseUrl = ref.read(serverUrlProvider); - final markdown = '![${cloudFile.name}]($baseUrl/files/${cloudFile.id})'; + final markdown = '![${cloudFile.name}](solian://files/${cloudFile.id})'; final controller = state.contentController; final text = controller.text; final selection = controller.selection; diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart index 6a373e4..e9f1fab 100644 --- a/lib/widgets/post/post_item.dart +++ b/lib/widgets/post/post_item.dart @@ -331,6 +331,7 @@ class PostItem extends HookConsumerWidget { item.type == 0 ? EdgeInsets.only(bottom: 8) : null, + attachments: item.attachments, ), ], // Render tags and categories if they exist @@ -689,6 +690,7 @@ Widget _buildReferencePost(BuildContext context, SnPost item) { referencePost.type == 0 ? EdgeInsets.only(bottom: 4) : null, + attachments: item.attachments, ).padding(bottom: 4), // Truncation hint for referenced post if (referencePost.isTruncated) @@ -696,7 +698,8 @@ Widget _buildReferencePost(BuildContext context, SnPost item) { isCompact: true, margin: const EdgeInsets.only(top: 4, bottom: 8), ), - if (referencePost.attachments.isNotEmpty && referencePost.type != 1) + if (referencePost.attachments.isNotEmpty && + referencePost.type != 1) Row( mainAxisSize: MainAxisSize.min, children: [ @@ -1030,6 +1033,7 @@ class _ArticlePostDisplay extends StatelessWidget { MarkdownTextContent( content: item.content!, textStyle: Theme.of(context).textTheme.bodyLarge, + attachments: item.attachments, ), ], );