💄 Optimize the article editor

This commit is contained in:
2026-01-17 14:50:16 +08:00
parent 09767e113f
commit c5d667ecf3
3 changed files with 99 additions and 86 deletions

View File

@@ -201,6 +201,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: colorScheme.surface,
border: Border.all(color: colorScheme.outline.withOpacity(0.3)), border: Border.all(color: colorScheme.outline.withOpacity(0.3)),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
@@ -225,19 +226,14 @@ class ArticleComposeScreen extends HookConsumerWidget {
], ],
), ),
), ),
Expanded( Expanded(child: widgetItem),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: widgetItem,
),
),
], ],
), ),
); );
} }
Widget buildEditorPane() { Widget buildEditorPane() {
return Center( final editorContent = Center(
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 560), constraints: const BoxConstraints(maxWidth: 560),
child: Column( child: Column(
@@ -267,6 +263,20 @@ class ArticleComposeScreen extends HookConsumerWidget {
), ),
), ),
); );
// Add background color for mobile editor pane
if (!isWideScreen(context)) {
return Container(
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(8),
),
margin: const EdgeInsets.symmetric(vertical: 8),
child: editorContent,
);
}
return editorContent;
} }
return PopScope( return PopScope(
@@ -325,7 +335,9 @@ class ArticleComposeScreen extends HookConsumerWidget {
Tooltip( Tooltip(
message: 'togglePreview'.tr(), message: 'togglePreview'.tr(),
child: IconButton( child: IconButton(
icon: Icon(showPreview.value ? Symbols.edit : Symbols.preview), icon: Icon(
showPreview.value ? Symbols.preview_off : Symbols.preview,
),
onPressed: () => showPreview.value = !showPreview.value, onPressed: () => showPreview.value = !showPreview.value,
), ),
), ),
@@ -395,12 +407,25 @@ class ArticleComposeScreen extends HookConsumerWidget {
switchOutCurve: Curves.easeOutCubic, switchOutCurve: Curves.easeOutCubic,
transitionBuilder: transitionBuilder:
(Widget child, Animation<double> animation) { (Widget child, Animation<double> animation) {
return FadeTransition( final isWide = isWideScreen(context);
opacity: animation, if (isWide) {
child: SlideTransition( // Desktop: scale animation
return ScaleTransition(
scale: Tween<double>(begin: 0.95, end: 1.0)
.animate(
CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
),
),
child: child,
);
} else {
// Mobile: horizontal slide animation
return SlideTransition(
position: position:
Tween<Offset>( Tween<Offset>(
begin: const Offset(0, 0.05), begin: const Offset(0.05, 0),
end: Offset.zero, end: Offset.zero,
).animate( ).animate(
CurvedAnimation( CurvedAnimation(
@@ -409,8 +434,8 @@ class ArticleComposeScreen extends HookConsumerWidget {
), ),
), ),
child: child, child: child,
), );
); }
}, },
child: isWideScreen(context) child: isWideScreen(context)
? Row( ? Row(
@@ -439,4 +464,4 @@ class ArticleComposeScreen extends HookConsumerWidget {
), ),
); );
} }
} }

View File

@@ -44,7 +44,7 @@ class ResponsiveSidebar extends HookConsumerWidget {
showDrawer.value = true; showDrawer.value = true;
animationController.forward(); animationController.forward();
} else if (!showSidebar.value && showDrawer.value) { } else if (!showSidebar.value && showDrawer.value) {
showDrawer.value = false; // Don't set showDrawer.value = false here - let animation complete first
animationController.reverse(); animationController.reverse();
} }
} else { } else {

View File

@@ -121,11 +121,10 @@ class PostComposeCard extends HookConsumerWidget {
content: composeState.contentController.text, content: composeState.contentController.text,
visibility: composeState.visibility.value, visibility: composeState.visibility.value,
type: composeState.postType, type: composeState.postType,
attachments: attachments: composeState.attachments.value
composeState.attachments.value .where((e) => e.isOnCloud)
.where((e) => e.isOnCloud) .map((e) => e.data as SnCloudFile)
.map((e) => e.data as SnCloudFile) .toList(),
.toList(),
publisher: composeState.currentPublisher.value!, publisher: composeState.currentPublisher.value!,
updatedAt: DateTime.now(), updatedAt: DateTime.now(),
); );
@@ -218,27 +217,25 @@ class PostComposeCard extends HookConsumerWidget {
IconButton( IconButton(
onPressed: onPressed:
(composeState.submitting.value || (composeState.submitting.value ||
composeState.currentPublisher.value == null) composeState.currentPublisher.value == null)
? null ? null
: performSubmit, : performSubmit,
icon: icon: composeState.submitting.value
composeState.submitting.value ? SizedBox(
? SizedBox( width: 24,
width: 24, height: 24,
height: 24, child: const CircularProgressIndicator(
child: const CircularProgressIndicator( strokeWidth: 2,
strokeWidth: 2,
),
)
: Icon(
originalPost != null
? Symbols.edit
: Symbols.upload,
), ),
tooltip: )
originalPost != null : Icon(
? 'postUpdate'.tr() originalPost != null
: 'postPublish'.tr(), ? Symbols.edit
: Symbols.upload,
),
tooltip: originalPost != null
? 'postUpdate'.tr()
: 'postPublish'.tr(),
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
horizontal: -4, horizontal: -4,
vertical: -2, vertical: -2,
@@ -268,13 +265,10 @@ class PostComposeCard extends HookConsumerWidget {
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
useRootNavigator: true, useRootNavigator: true,
builder: builder: (context) => SheetScaffold(
(context) => SheetScaffold( titleText: 'Post Preview',
titleText: 'Post Preview', child: SingleChildScrollView(child: PostItem(item: post)),
child: SingleChildScrollView( ),
child: PostItem(item: post),
),
),
); );
}, },
), ),
@@ -283,16 +277,15 @@ class PostComposeCard extends HookConsumerWidget {
Expanded( Expanded(
child: KeyboardListener( child: KeyboardListener(
focusNode: FocusNode(), focusNode: FocusNode(),
onKeyEvent: onKeyEvent: (event) => ComposeLogic.handleKeyPress(
(event) => ComposeLogic.handleKeyPress( event,
event, composeState,
composeState, ref,
ref, context,
context, originalPost: originalPost,
originalPost: originalPost, repliedPost: repliedPost,
repliedPost: repliedPost, forwardedPost: forwardedPost,
forwardedPost: forwardedPost, ),
),
child: SingleChildScrollView( child: SingleChildScrollView(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: ConstrainedBox( child: ConstrainedBox(
@@ -304,17 +297,16 @@ class PostComposeCard extends HookConsumerWidget {
// Publisher profile picture // Publisher profile picture
GestureDetector( GestureDetector(
child: ProfilePictureWidget( child: ProfilePictureWidget(
fileId: fileId: composeState
composeState .currentPublisher
.currentPublisher .value
.value ?.picture
?.picture ?.id,
?.id,
radius: 20, radius: 20,
fallbackIcon: fallbackIcon:
composeState.currentPublisher.value == null composeState.currentPublisher.value == null
? Symbols.question_mark ? Symbols.question_mark
: null, : null,
), ),
onTap: () { onTap: () {
if (composeState.currentPublisher.value == null) { if (composeState.currentPublisher.value == null) {
@@ -326,8 +318,8 @@ class PostComposeCard extends HookConsumerWidget {
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
useRootNavigator: true, useRootNavigator: true,
builder: builder: (context) =>
(context) => const NewPublisherScreen(), const NewPublisherScreen(),
).then((value) { ).then((value) {
if (value != null) { if (value != null) {
composeState.currentPublisher.value = composeState.currentPublisher.value =
@@ -370,9 +362,8 @@ class PostComposeCard extends HookConsumerWidget {
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
useRootNavigator: true, useRootNavigator: true,
builder: builder: (context) =>
(context) => const NewPublisherScreen(),
const NewPublisherScreen(),
).then((value) { ).then((value) {
if (value != null) { if (value != null) {
composeState.currentPublisher.value = composeState.currentPublisher.value =
@@ -388,8 +379,8 @@ class PostComposeCard extends HookConsumerWidget {
isScrollControlled: true, isScrollControlled: true,
useRootNavigator: true, useRootNavigator: true,
context: context, context: context,
builder: builder: (context) =>
(context) => const PublisherModal(), const PublisherModal(),
).then((value) { ).then((value) {
if (value != null) { if (value != null) {
composeState.currentPublisher.value = composeState.currentPublisher.value =
@@ -415,19 +406,16 @@ class PostComposeCard extends HookConsumerWidget {
), ),
// Bottom toolbar // Bottom toolbar
SizedBox( ClipRRect(
height: 65, borderRadius: const BorderRadius.only(
child: ClipRRect( bottomLeft: Radius.circular(8),
borderRadius: const BorderRadius.only( bottomRight: Radius.circular(8),
bottomLeft: Radius.circular(8), ),
bottomRight: Radius.circular(8), child: ComposeToolbar(
), state: composeState,
child: ComposeToolbar( originalPost: originalPost,
state: composeState, isCompact: true,
originalPost: originalPost, useSafeArea: isContained,
isCompact: true,
useSafeArea: isContained,
),
), ),
), ),
], ],