💄 Optimized post editor

This commit is contained in:
LittleSheep 2024-08-16 21:06:50 +08:00
parent af93a8386a
commit cf767a1d94
5 changed files with 132 additions and 100 deletions

View File

@ -115,7 +115,7 @@ PODS:
- TOCropViewController (~> 2.7.4) - TOCropViewController (~> 2.7.4)
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- livekit_client (2.2.3): - livekit_client (2.2.4):
- Flutter - Flutter
- WebRTC-SDK (= 125.6422.04) - WebRTC-SDK (= 125.6422.04)
- media_kit_libs_ios_video (1.0.4): - media_kit_libs_ios_video (1.0.4):
@ -306,7 +306,7 @@ SPEC CHECKSUMS:
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
livekit_client: bad83a7776a41abc42e1f26d903eeac9164c8a9f livekit_client: d079c5f040d4bf2b80440ff0ae997725a183e4bc
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e

View File

@ -260,37 +260,104 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: ListView( child: Column(
children: [ children: [
if (_isBusy) Expanded(
const LinearProgressIndicator().animate().scaleX(), child: ListView(
Container( children: [
padding: const EdgeInsets.symmetric( if (_isBusy)
horizontal: 16, const LinearProgressIndicator()
vertical: 8, .animate()
), .scaleX(),
child: TextField( Container(
maxLines: null, padding: const EdgeInsets.symmetric(
autofocus: true, horizontal: 16,
autocorrect: true, vertical: 8,
keyboardType: TextInputType.multiline, ),
controller: _editorController.contentController, child: TextField(
focusNode: _contentFocusNode, maxLines: null,
decoration: InputDecoration.collapsed( autofocus: true,
hintText: 'postContentPlaceholder'.tr, autocorrect: true,
), keyboardType: TextInputType.multiline,
onTapOutside: (_) => controller:
FocusManager.instance.primaryFocus?.unfocus(), _editorController.contentController,
focusNode: _contentFocusNode,
decoration: InputDecoration.collapsed(
hintText: 'postContentPlaceholder'.tr,
),
onTapOutside: (_) => FocusManager
.instance.primaryFocus
?.unfocus(),
),
),
const SizedBox(height: 120)
],
), ),
), ),
const SizedBox(height: 120) Obx(() {
final textStyle = TextStyle(
fontSize: 12,
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.75),
);
final showFactors = [
_editorController.isRestoreFromLocal.value,
_editorController.lastSaveTime.value != null,
];
final doShow = showFactors.any((x) => x);
return Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 16,
),
child: Row(
children: [
if (showFactors[0])
Text('postRestoreFromLocal'.tr,
style: textStyle)
.paddingOnly(right: 4),
if (showFactors[0])
InkWell(
child: Text('clear'.tr, style: textStyle),
onTap: () {
_editorController.localClear();
_editorController.currentClear();
setState(() {});
},
),
if (showFactors.where((x) => x).length > 1)
Text(
'·',
style: textStyle,
).paddingSymmetric(horizontal: 8),
if (showFactors[1])
Text(
'postAutoSaveAt'.trParams({
'date': DateFormat('HH:mm:ss').format(
_editorController.lastSaveTime.value ??
DateTime.now(),
)
}),
style: textStyle,
),
],
),
)
.animate(
key: const Key('post-editor-hint-animation'),
target: doShow ? 1 : 0,
)
.fade(curve: Curves.easeInOut, duration: 300.ms);
}),
], ],
), ),
), ),
if (SolianTheme.isLargeScreen(context)) if (SolianTheme.isLargeScreen(context))
const VerticalDivider(width: 0.3, thickness: 0.3) const VerticalDivider(width: 0.3, thickness: 0.3)
.paddingSymmetric( .paddingSymmetric(
horizontal: 8, horizontal: 16,
), ),
if (SolianTheme.isLargeScreen(context)) if (SolianTheme.isLargeScreen(context))
Expanded( Expanded(
@ -298,7 +365,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
child: MarkdownTextContent( child: MarkdownTextContent(
content: _editorController.contentController.text, content: _editorController.contentController.text,
parentId: 'post-editor-preview', parentId: 'post-editor-preview',
).paddingOnly(top: 8), ).paddingOnly(top: 12, right: 16),
), ),
), ),
], ],
@ -309,85 +376,39 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Obx(() { const Divider(thickness: 0.3, height: 0.3),
final textStyle = TextStyle(
fontSize: 12,
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.75),
);
final showFactors = [
_editorController.isRestoreFromLocal.value,
_editorController.lastSaveTime.value != null,
];
final doShow = showFactors.any((x) => x);
return Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 16,
),
child: Row(
children: [
if (showFactors[0])
Text('postRestoreFromLocal'.tr, style: textStyle)
.paddingOnly(right: 4),
if (showFactors[0])
InkWell(
child: Text('clear'.tr, style: textStyle),
onTap: () {
_editorController.localClear();
_editorController.currentClear();
setState(() {});
},
),
if (showFactors.where((x) => x).length > 1)
Text(
'·',
style: textStyle,
).paddingSymmetric(horizontal: 8),
if (showFactors[1])
Text(
'postAutoSaveAt'.trParams({
'date': DateFormat('HH:mm:ss').format(
_editorController.lastSaveTime.value ??
DateTime.now(),
)
}),
style: textStyle,
),
],
),
)
.animate(
key: const Key('post-editor-hint-animation'),
target: doShow ? 1 : 0,
)
.fade(curve: Curves.easeInOut, duration: 300.ms);
}),
if (_editorController.mode.value == 0)
Obx(
() => TweenAnimationBuilder<double>(
tween: Tween(
begin: 0,
end: _editorController.contentLength.value / 4096,
),
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
builder: (context, value, _) => LinearProgressIndicator(
minHeight: 2,
color: _editorController.contentLength.value > 4096
? Colors.red[900]
: Theme.of(context).colorScheme.primary,
value: value,
),
),
),
SizedBox( SizedBox(
height: 56, height: 56,
child: ListView( child: ListView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
children: [ children: [
if (_editorController.mode.value == 0)
Obx(
() => TweenAnimationBuilder<double>(
tween: Tween(
begin: 0,
end: _editorController.contentLength.value /
4096,
),
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
builder: (context, value, _) => SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 3,
backgroundColor: Theme.of(context)
.colorScheme
.secondaryContainer,
color: _editorController.contentLength.value >
4096
? Colors.red[900]
: Theme.of(context).colorScheme.primary,
value: value,
),
).paddingAll(10),
),
).paddingSymmetric(horizontal: 4),
Obx(() { Obx(() {
final isDraft = _editorController.isDraft.value; final isDraft = _editorController.isDraft.value;
return IconButton( return IconButton(

View File

@ -15,6 +15,7 @@ class AttachmentItem extends StatefulWidget {
final Attachment item; final Attachment item;
final bool showBadge; final bool showBadge;
final bool showHideButton; final bool showHideButton;
final bool autoload;
final BoxFit fit; final BoxFit fit;
final String? badge; final String? badge;
final Function? onHide; final Function? onHide;
@ -27,6 +28,7 @@ class AttachmentItem extends StatefulWidget {
this.fit = BoxFit.cover, this.fit = BoxFit.cover,
this.showBadge = true, this.showBadge = true,
this.showHideButton = true, this.showHideButton = true,
this.autoload = false,
this.onHide, this.onHide,
}); });
@ -49,7 +51,10 @@ class _AttachmentItemState extends State<AttachmentItem> {
onHide: widget.onHide, onHide: widget.onHide,
); );
case 'video': case 'video':
return _AttachmentItemVideo(item: widget.item); return _AttachmentItemVideo(
item: widget.item,
autoload: widget.autoload,
);
default: default:
return Center( return Center(
child: Container( child: Container(

View File

@ -17,6 +17,7 @@ class AttachmentList extends StatefulWidget {
final List<int> attachmentsId; final List<int> attachmentsId;
final bool isGrid; final bool isGrid;
final bool isForceGrid; final bool isForceGrid;
final bool autoload;
final double flatMaxHeight; final double flatMaxHeight;
final double? width; final double? width;
@ -28,6 +29,7 @@ class AttachmentList extends StatefulWidget {
required this.attachmentsId, required this.attachmentsId,
this.isGrid = false, this.isGrid = false,
this.isForceGrid = false, this.isForceGrid = false,
this.autoload = false,
this.flatMaxHeight = 720, this.flatMaxHeight = 720,
this.width, this.width,
this.viewport, this.viewport,
@ -208,6 +210,7 @@ class AttachmentListEntry extends StatelessWidget {
final bool showBadge; final bool showBadge;
final bool showMature; final bool showMature;
final bool isDense; final bool isDense;
final bool autoload;
final Function(bool) onReveal; final Function(bool) onReveal;
const AttachmentListEntry({ const AttachmentListEntry({
@ -221,6 +224,7 @@ class AttachmentListEntry extends StatelessWidget {
this.showBadge = false, this.showBadge = false,
this.showMature = false, this.showMature = false,
this.isDense = false, this.isDense = false,
this.autoload = false,
}); });
@override @override
@ -259,6 +263,7 @@ class AttachmentListEntry extends StatelessWidget {
item: item!, item: item!,
badge: showBadge ? badgeContent : null, badge: showBadge ? badgeContent : null,
showHideButton: !item!.isMature || showMature, showHideButton: !item!.isMature || showMature,
autoload: autoload,
onHide: () { onHide: () {
onReveal(false); onReveal(false);
}, },

View File

@ -465,6 +465,7 @@ class _PostItemState extends State<PostItem> {
flatMaxHeight: MediaQuery.of(context).size.width, flatMaxHeight: MediaQuery.of(context).size.width,
parentId: widget.item.id.toString(), parentId: widget.item.id.toString(),
attachmentsId: attachments, attachmentsId: attachments,
autoload: true,
isGrid: attachments.length > 1, isGrid: attachments.length > 1,
), ),
if (widget.isShowReply || widget.isReactable) if (widget.isShowReply || widget.isReactable)