♻️ Refactored attachment loading system

This commit is contained in:
2024-12-26 22:19:01 +08:00
parent 619c90cdd9
commit 7656c08832
15 changed files with 341 additions and 276 deletions

View File

@ -99,10 +99,10 @@ class _AttachmentInputDialogState extends State<AttachmentInputDialog> {
),
actions: [
TextButton(
child: Text('dialogDismiss').tr(),
onPressed: _isBusy ? null : () {
Navigator.pop(context);
},
child: Text('dialogDismiss').tr(),
),
TextButton(
onPressed: _isBusy ? null : () => _finishUp(),

View File

@ -18,6 +18,7 @@ import 'package:uuid/uuid.dart';
class AttachmentItem extends StatelessWidget {
final SnAttachment? data;
final String? heroTag;
const AttachmentItem({
super.key,
required this.data,
@ -60,9 +61,14 @@ class AttachmentItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (data!.isMature) {
return _AttachmentItemSensitiveBlur(
child: _buildContent(context),
if (data!.contentRating > 0) {
return LayoutBuilder(
builder: (context, constraints) {
return _AttachmentItemSensitiveBlur(
isCompact: constraints.maxHeight < 360,
child: _buildContent(context),
);
}
);
}
@ -72,15 +78,15 @@ class AttachmentItem extends StatelessWidget {
class _AttachmentItemSensitiveBlur extends StatefulWidget {
final Widget child;
const _AttachmentItemSensitiveBlur({super.key, required this.child});
final bool isCompact;
const _AttachmentItemSensitiveBlur({super.key, required this.child, this.isCompact = false});
@override
State<_AttachmentItemSensitiveBlur> createState() =>
_AttachmentItemSensitiveBlurState();
State<_AttachmentItemSensitiveBlur> createState() => _AttachmentItemSensitiveBlurState();
}
class _AttachmentItemSensitiveBlurState
extends State<_AttachmentItemSensitiveBlur> {
class _AttachmentItemSensitiveBlurState extends State<_AttachmentItemSensitiveBlur> {
bool _doesShow = false;
@override
@ -104,24 +110,21 @@ class _AttachmentItemSensitiveBlurState
color: Colors.white,
size: 32,
),
const Gap(8),
Text('sensitiveContent', textAlign: TextAlign.center)
.tr()
.fontSize(20)
.textColor(Colors.white)
.bold(),
Text(
'sensitiveContentDescription',
textAlign: TextAlign.center,
)
.tr()
.fontSize(14)
.textColor(Colors.white.withOpacity(0.8)),
const Gap(16),
InkWell(
child: Text('sensitiveContentReveal')
if (!widget.isCompact) const Gap(8),
if (!widget.isCompact)
Text('sensitiveContent', textAlign: TextAlign.center)
.tr()
.textColor(Colors.white),
.fontSize(20)
.textColor(Colors.white)
.bold(),
if (!widget.isCompact)
Text(
'sensitiveContentDescription',
textAlign: TextAlign.center,
).tr().fontSize(14).textColor(Colors.white.withOpacity(0.8)),
if (!widget.isCompact) const Gap(16),
InkWell(
child: Text('sensitiveContentReveal').tr().textColor(Colors.white),
onTap: () {
setState(() => _doesShow = !_doesShow);
},
@ -131,9 +134,7 @@ class _AttachmentItemSensitiveBlurState
).center(),
),
),
)
.opacity(_doesShow ? 0 : 1, animate: true)
.animate(const Duration(milliseconds: 300), Curves.easeInOut),
).opacity(_doesShow ? 0 : 1, animate: true).animate(const Duration(milliseconds: 300), Curves.easeInOut),
if (_doesShow)
Positioned(
top: 0,
@ -163,6 +164,7 @@ class _AttachmentItemSensitiveBlurState
class _AttachmentItemContentVideo extends StatefulWidget {
final SnAttachment data;
final bool isAutoload;
const _AttachmentItemContentVideo({
super.key,
required this.data,
@ -170,12 +172,10 @@ class _AttachmentItemContentVideo extends StatefulWidget {
});
@override
State<_AttachmentItemContentVideo> createState() =>
_AttachmentItemContentVideoState();
State<_AttachmentItemContentVideo> createState() => _AttachmentItemContentVideoState();
}
class _AttachmentItemContentVideoState
extends State<_AttachmentItemContentVideo> {
class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo> {
bool _showContent = false;
Player? _videoPlayer;
@ -266,10 +266,7 @@ class _AttachmentItemContentVideoState
),
Text(
Duration(
milliseconds:
(widget.data.metadata['duration'] ?? 0)
.toInt() *
1000,
milliseconds: (widget.data.metadata['duration'] ?? 0).toInt() * 1000,
).toString(),
style: GoogleFonts.robotoMono(
fontSize: 12,
@ -317,6 +314,7 @@ class _AttachmentItemContentVideoState
class _AttachmentItemContentAudio extends StatefulWidget {
final SnAttachment data;
final bool isAutoload;
const _AttachmentItemContentAudio({
super.key,
required this.data,
@ -324,12 +322,10 @@ class _AttachmentItemContentAudio extends StatefulWidget {
});
@override
State<_AttachmentItemContentAudio> createState() =>
_AttachmentItemContentAudioState();
State<_AttachmentItemContentAudio> createState() => _AttachmentItemContentAudioState();
}
class _AttachmentItemContentAudioState
extends State<_AttachmentItemContentAudio> {
class _AttachmentItemContentAudioState extends State<_AttachmentItemContentAudio> {
bool _showContent = false;
double? _draggingValue;
@ -499,12 +495,8 @@ class _AttachmentItemContentAudioState
overlayShape: SliderComponentShape.noOverlay,
),
child: Slider(
secondaryTrackValue: _bufferedPosition
.inMilliseconds
.abs()
.toDouble(),
value: _draggingValue?.abs() ??
_position.inMilliseconds.toDouble().abs(),
secondaryTrackValue: _bufferedPosition.inMilliseconds.abs().toDouble(),
value: _draggingValue?.abs() ?? _position.inMilliseconds.toDouble().abs(),
min: 0,
max: math
.max(
@ -544,9 +536,7 @@ class _AttachmentItemContentAudioState
),
const Gap(16),
IconButton.filled(
icon: _isPlaying
? const Icon(Symbols.pause)
: const Icon(Symbols.play_arrow),
icon: _isPlaying ? const Icon(Symbols.pause) : const Icon(Symbols.play_arrow),
onPressed: () {
_audioPlayer!.playOrPause();
},

View File

@ -58,7 +58,7 @@ class _AttachmentListState extends State<AttachmentList> {
if (widget.data.isEmpty) return const SizedBox.shrink();
if (widget.data.length == 1) {
final singleAspectRatio = widget.data[0]?.metadata['ratio']?.toDouble() ??
final singleAspectRatio = widget.data[0]?.data['ratio']?.toDouble() ??
switch (widget.data[0]?.mimetype.split('/').firstOrNull) {
'audio' => 16 / 9,
'video' => 16 / 9,
@ -114,6 +114,7 @@ class _AttachmentListState extends State<AttachmentList> {
},
),
onTap: () {
if (widget.data.firstOrNull?.mediaType != SnMediaType.image) return;
context.pushTransparentRoute(
AttachmentZoomView(
data: widget.data.where((ele) => ele != null).cast(),
@ -136,7 +137,7 @@ class _AttachmentListState extends State<AttachmentList> {
children: widget.data
.mapIndexed(
(idx, ele) => AspectRatio(
aspectRatio: (ele?.metadata['ratio'] ?? 1).toDouble(),
aspectRatio: (ele?.data['ratio'] ?? 1).toDouble(),
child: Container(
decoration: BoxDecoration(
color: backgroundColor,
@ -161,7 +162,7 @@ class _AttachmentListState extends State<AttachmentList> {
}
return AspectRatio(
aspectRatio: (widget.data.firstOrNull?.metadata['ratio'] ?? 1).toDouble(),
aspectRatio: (widget.data.firstOrNull?.data['ratio'] ?? 1).toDouble(),
child: Container(
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
child: ScrollConfiguration(
@ -173,12 +174,14 @@ class _AttachmentListState extends State<AttachmentList> {
return Container(
constraints: constraints,
child: AspectRatio(
aspectRatio: (widget.data[idx]?.metadata['ratio'] ?? 1).toDouble(),
aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
child: GestureDetector(
onTap: () {
if (widget.data[idx]?.mediaType != SnMediaType.image) return;
context.pushTransparentRoute(
AttachmentZoomView(
data: widget.data.where((ele) => ele != null).cast(),
data:
widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(),
initialIndex: idx,
heroTags: heroTags,
),

View File

@ -1,18 +1,14 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:image_picker/image_picker.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:pasteboard/pasteboard.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/controllers/chat_message_controller.dart';
import 'package:surface/controllers/post_write_controller.dart';
import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/attachment.dart';
import 'package:surface/types/chat.dart';
import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/markdown_content.dart';
@ -80,7 +76,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
media.name,
'messaging',
null,
mimetype: media.raw != null && media.type == PostWriteMediaType.image ? 'image/png' : null,
mimetype: media.raw != null && media.type == SnMediaType.image ? 'image/png' : null,
);
final item = await attach.chunkedUploadParts(

View File

@ -124,7 +124,7 @@ class PostMediaPendingList extends StatelessWidget {
ContextMenu _createContextMenu(BuildContext context, int idx, PostWriteMedia media) {
return ContextMenu(
entries: [
if (media.attachment != null && media.type == PostWriteMediaType.video)
if (media.attachment != null && media.type == SnMediaType.video)
MenuItem(
label: 'attachmentSetThumbnail'.tr(),
icon: Symbols.image,
@ -140,7 +140,7 @@ class PostMediaPendingList extends StatelessWidget {
onUpload!(idx);
}),
if (media.attachment != null &&
media.type == PostWriteMediaType.image &&
media.type == SnMediaType.image &&
onPostSetThumbnail != null &&
idx != -1)
MenuItem(
@ -150,7 +150,7 @@ class PostMediaPendingList extends StatelessWidget {
onPostSetThumbnail!(idx);
},
)
else if (media.attachment != null && media.type == PostWriteMediaType.image && onPostSetThumbnail != null)
else if (media.attachment != null && media.type == SnMediaType.image && onPostSetThumbnail != null)
MenuItem(
label: 'attachmentUnsetAsPostThumbnail'.tr(),
icon: Symbols.cancel,
@ -166,7 +166,7 @@ class PostMediaPendingList extends StatelessWidget {
onInsertLink!(idx);
},
),
if (media.type == PostWriteMediaType.image && media.attachment != null)
if (media.type == SnMediaType.image && media.attachment != null)
MenuItem(
label: 'preview'.tr(),
icon: Symbols.preview,
@ -177,7 +177,7 @@ class PostMediaPendingList extends StatelessWidget {
);
},
),
if (media.type == PostWriteMediaType.image && media.attachment == null)
if (media.type == SnMediaType.image && media.attachment == null)
MenuItem(
label: 'crop'.tr(),
icon: Symbols.crop,
@ -219,10 +219,6 @@ class PostMediaPendingList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
final sn = context.read<SnNetworkProvider>();
return Container(
constraints: const BoxConstraints(maxHeight: 120),
child: Row(
@ -285,7 +281,7 @@ class _PostMediaPendingItem extends StatelessWidget {
child: AspectRatio(
aspectRatio: 1,
child: switch (media.type) {
PostWriteMediaType.image => Container(
SnMediaType.image => Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: LayoutBuilder(builder: (context, constraints) {
return Image(
@ -298,7 +294,7 @@ class _PostMediaPendingItem extends StatelessWidget {
);
}),
),
PostWriteMediaType.video => Container(
SnMediaType.video => Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: media.attachment?.metadata['thumbnail'] != null
? AutoResizeUniversalImage(sn.getAttachmentUrl(media.attachment?.metadata['thumbnail']))
@ -345,7 +341,7 @@ class AddPostMediaButton extends StatelessWidget {
PostWriteMedia.fromBytes(
imageBytes,
'attachmentPastedImage'.tr(),
PostWriteMediaType.image,
SnMediaType.image,
),
]);
}