Compare commits

...

2 Commits

Author SHA1 Message Date
LittleSheep
6c48aa2356 💄 Optimize attachment preview 2025-08-17 13:08:08 +08:00
LittleSheep
466e354679 💄 Optimize attachment in article 2025-08-17 13:02:52 +08:00
4 changed files with 106 additions and 77 deletions

View File

@@ -841,5 +841,8 @@
"messageJumpNotLoaded": "The referenced message was not loaded, unable to jump to it.", "messageJumpNotLoaded": "The referenced message was not loaded, unable to jump to it.",
"postUnlinkRealm": "No linked realm", "postUnlinkRealm": "No linked realm",
"postSlug": "Slug", "postSlug": "Slug",
"postSlugHint": "The slug can be used to access your post via URL in the webpage, it should be publisher-wide unique." "postSlugHint": "The slug can be used to access your post via URL in the webpage, it should be publisher-wide unique.",
"attachmentOnDevice": "On-device",
"attachmentOnCloud": "On-cloud",
"attachments": "Attachments"
} }

View File

@@ -816,5 +816,7 @@
"messageJumpNotLoaded": "引用的消息没有被加载,无法跳转。", "messageJumpNotLoaded": "引用的消息没有被加载,无法跳转。",
"postUnlinkRealm": "不关联领域", "postUnlinkRealm": "不关联领域",
"postSlug": "别名", "postSlug": "别名",
"postSlugHint": "这个别名可以用于在网页通过 URL 浏览到你的帖子,它应该在同一发布者中是唯一。" "postSlugHint": "这个别名可以用于在网页通过 URL 浏览到你的帖子,它应该在同一发布者中是唯一。",
"attachmentOnDevice": "离线",
"attachmentOnCloud": "在线"
} }

View File

@@ -89,6 +89,9 @@ class ArticleComposeScreen extends HookConsumerWidget {
}, [state]); }, [state]);
final showPreview = useState(false); final showPreview = useState(false);
final isAttachmentsExpanded = useState(
true,
); // New state for attachments section
// Initialize publisher once when data is available // Initialize publisher once when data is available
useEffect(() { useEffect(() {
@@ -297,71 +300,88 @@ class ArticleComposeScreen extends HookConsumerWidget {
valueListenable: state.attachments, valueListenable: state.attachments,
builder: (context, attachments, _) { builder: (context, attachments, _) {
if (attachments.isEmpty) return const SizedBox.shrink(); if (attachments.isEmpty) return const SizedBox.shrink();
return Column( return Theme(
crossAxisAlignment: CrossAxisAlignment.start, data: Theme.of(
children: [ context,
const Gap(16), ).copyWith(dividerColor: Colors.transparent),
Text( child: ExpansionTile(
'articleAttachmentHint'.tr(), initiallyExpanded: isAttachmentsExpanded.value,
style: Theme.of(context).textTheme.bodySmall?.copyWith( onExpansionChanged: (expanded) {
color: Theme.of(context).colorScheme.onSurfaceVariant, isAttachmentsExpanded.value = expanded;
), },
).padding(bottom: 8), collapsedBackgroundColor:
ValueListenableBuilder<Map<int, double>>( Theme.of(context).colorScheme.surfaceContainer,
valueListenable: state.attachmentProgress, title: Column(
builder: (context, progressMap, _) { crossAxisAlignment: CrossAxisAlignment.start,
return Wrap( children: [
runSpacing: 8, Text('attachments').tr(),
spacing: 8, Text(
children: [ 'articleAttachmentHint'.tr(),
for (var idx = 0; idx < attachments.length; idx++) style: Theme.of(
SizedBox( context,
width: 280, ).textTheme.bodySmall?.copyWith(
height: 280, color:
child: AttachmentPreview( Theme.of(
item: attachments[idx], context,
progress: progressMap[idx], ).colorScheme.onSurfaceVariant,
onRequestUpload: ),
() => ComposeLogic.uploadAttachment( ),
ref, ],
state,
idx,
),
onUpdate:
(value) =>
ComposeLogic.updateAttachment(
state,
value,
idx,
),
onDelete:
() => ComposeLogic.deleteAttachment(
ref,
state,
idx,
),
onMove: (delta) {
state
.attachments
.value = ComposeLogic.moveAttachment(
state.attachments.value,
idx,
delta,
);
},
onInsert:
() => ComposeLogic.insertAttachment(
ref,
state,
idx,
),
),
),
],
);
},
), ),
], children: [
ValueListenableBuilder<Map<int, double>>(
valueListenable: state.attachmentProgress,
builder: (context, progressMap, _) {
return Wrap(
runSpacing: 8,
spacing: 8,
children: [
for (
var idx = 0;
idx < attachments.length;
idx++
)
SizedBox(
width: 180,
height: 180,
child: AttachmentPreview(
isCompact: true,
item: attachments[idx],
progress: progressMap[idx],
onRequestUpload:
() => ComposeLogic.uploadAttachment(
ref,
state,
idx,
),
onUpdate:
(value) =>
ComposeLogic.updateAttachment(
state,
value,
idx,
),
onDelete:
() => ComposeLogic.deleteAttachment(
ref,
state,
idx,
),
onInsert:
() => ComposeLogic.insertAttachment(
ref,
state,
idx,
),
),
),
],
);
},
),
Gap(16),
],
),
); );
}, },
), ),

View File

@@ -88,6 +88,7 @@ class AttachmentPreview extends HookConsumerWidget {
final Function? onInsert; final Function? onInsert;
final Function(UniversalFile)? onUpdate; final Function(UniversalFile)? onUpdate;
final Function? onRequestUpload; final Function? onRequestUpload;
final bool isCompact;
const AttachmentPreview({ const AttachmentPreview({
super.key, super.key,
@@ -98,6 +99,7 @@ class AttachmentPreview extends HookConsumerWidget {
this.onDelete, this.onDelete,
this.onUpdate, this.onUpdate,
this.onInsert, this.onInsert,
this.isCompact = false,
}); });
// GlobalKey for selector // GlobalKey for selector
@@ -361,7 +363,7 @@ class AttachmentPreview extends HookConsumerWidget {
), ),
], ],
), ),
), ).center(),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@@ -458,11 +460,12 @@ class AttachmentPreview extends HookConsumerWidget {
size: 16, size: 16,
color: Colors.white, color: Colors.white,
), ),
const Gap(8), if (!isCompact) const Gap(8),
Text( if (!isCompact)
'On-cloud', Text(
style: TextStyle(color: Colors.white), 'attachmentOnCloud'.tr(),
), style: TextStyle(color: Colors.white),
),
], ],
) )
: Row( : Row(
@@ -473,11 +476,12 @@ class AttachmentPreview extends HookConsumerWidget {
size: 16, size: 16,
color: Colors.white, color: Colors.white,
), ),
const Gap(8), if (!isCompact) const Gap(8),
Text( if (!isCompact)
'On-device', Text(
style: TextStyle(color: Colors.white), 'attachmentOnDevice'.tr(),
), style: TextStyle(color: Colors.white),
),
], ],
), ),
), ),