From 8091c2b0cab693cbc27590edb1951ab62622dea6 Mon Sep 17 00:00:00 2001 From: divyanshu Date: Mon, 11 Nov 2024 02:02:02 +0530 Subject: [PATCH 1/3] changed the compose box max height to 20% of the screen height --- lib/widgets/compose_box.dart | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/widgets/compose_box.dart b/lib/widgets/compose_box.dart index d1f3b93583..136426d247 100644 --- a/lib/widgets/compose_box.dart +++ b/lib/widgets/compose_box.dart @@ -363,18 +363,19 @@ class _ContentInputState extends State<_ContentInput> with WidgetsBindingObserve } } + @override Widget build(BuildContext context) { ColorScheme colorScheme = Theme.of(context).colorScheme; + final screenHeight = MediaQuery.of(context).size.height; return InputDecorator( decoration: const InputDecoration(), child: ConstrainedBox( - constraints: const BoxConstraints( + constraints: BoxConstraints( minHeight: _sendButtonSize - 2 * _inputVerticalPadding, - // TODO constrain this adaptively (i.e. not hard-coded 200) - maxHeight: 200, + maxHeight: screenHeight * 0.2, ), child: ComposeAutocomplete( narrow: widget.narrow, @@ -389,9 +390,14 @@ class _ContentInputState extends State<_ContentInput> with WidgetsBindingObserve maxLines: null, textCapitalization: TextCapitalization.sentences, ); - }), - )); + }, + ), + ), + ); } + + + } /// The content input for _StreamComposeBox. From ef5274fe027e6e466b39ab20f59313cea20fea44 Mon Sep 17 00:00:00 2001 From: divyanshu Date: Thu, 14 Nov 2024 14:28:35 +0530 Subject: [PATCH 2/3] feat(compose_box): add preview mode for content input --- lib/widgets/compose_box.dart | 207 ++++++++++++++++++++++++++++++++--- 1 file changed, 194 insertions(+), 13 deletions(-) diff --git a/lib/widgets/compose_box.dart b/lib/widgets/compose_box.dart index 136426d247..0f6ee483a4 100644 --- a/lib/widgets/compose_box.dart +++ b/lib/widgets/compose_box.dart @@ -16,6 +16,7 @@ import '../model/store.dart'; import 'autocomplete.dart'; import 'dialog.dart'; import 'store.dart'; +import 'text.dart'; import 'theme.dart'; const double _inputVerticalPadding = 8; @@ -991,7 +992,7 @@ class _ComposeBoxContainer extends StatelessWidget { } } -class _ComposeBoxLayout extends StatelessWidget { +class _ComposeBoxLayout extends StatefulWidget { const _ComposeBoxLayout({ required this.topicInput, required this.contentInput, @@ -1006,6 +1007,22 @@ class _ComposeBoxLayout extends StatelessWidget { final ComposeContentController contentController; final FocusNode contentFocusNode; + @override + State<_ComposeBoxLayout> createState() => _ComposeBoxLayoutState(); +} + +class _ComposeBoxLayoutState extends State<_ComposeBoxLayout> { + bool isPreviewMode = false; + + void togglePreview() { + + setState(() { + isPreviewMode = !isPreviewMode; + widget.contentFocusNode.requestFocus(); + + }); + } + @override Widget build(BuildContext context) { ThemeData themeData = Theme.of(context); @@ -1013,10 +1030,9 @@ class _ComposeBoxLayout extends StatelessWidget { final inputThemeData = themeData.copyWith( inputDecorationTheme: InputDecorationTheme( - // Both [contentPadding] and [isDense] combine to make the layout compact. isDense: true, contentPadding: const EdgeInsets.symmetric( - horizontal: 12.0, vertical: _inputVerticalPadding), + horizontal: 10.0, vertical: _inputVerticalPadding), border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(4.0)), borderSide: BorderSide.none), @@ -1032,25 +1048,190 @@ class _ComposeBoxLayout extends StatelessWidget { child: Theme( data: inputThemeData, child: Column(children: [ - if (topicInput != null) topicInput!, - if (topicInput != null) const SizedBox(height: 8), - contentInput, - ]))), + if (widget.topicInput != null) widget.topicInput!, + if (widget.topicInput != null) const SizedBox(height: 8), + // Show input or preview box + isPreviewMode + ? PreviewBox( + content: widget.contentController.textNormalized, + ) + : widget.contentInput, + ]), + ), + ), const SizedBox(width: 8), - sendButton, + widget.sendButton, ]), Theme( data: themeData.copyWith( iconTheme: themeData.iconTheme.copyWith(color: colorScheme.onSurfaceVariant)), child: Row(children: [ - _AttachFileButton(contentController: contentController, contentFocusNode: contentFocusNode), - _AttachMediaButton(contentController: contentController, contentFocusNode: contentFocusNode), - _AttachFromCameraButton(contentController: contentController, contentFocusNode: contentFocusNode), - ])), - ])); + IconButton( + icon: Icon(isPreviewMode ? Icons.visibility_off : Icons.visibility), + tooltip: isPreviewMode + ? 'Switch to Edit Mode' + : 'Preview Content', + onPressed: togglePreview, + ), + _AttachFileButton( + contentController: widget.contentController, + contentFocusNode: widget.contentFocusNode, + ), + _AttachMediaButton( + contentController: widget.contentController, + contentFocusNode: widget.contentFocusNode, + ), + _AttachFromCameraButton( + contentController: widget.contentController, + contentFocusNode: widget.contentFocusNode, + ), + + ]), + ), + ]), + ); } } + + +class PreviewBox extends StatelessWidget { + const PreviewBox({super.key, required this.content}); + + final String content; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + + return Container( + constraints: const BoxConstraints( + minHeight: _sendButtonSize - 2 * _inputVerticalPadding, + maxHeight: 200, + ), + width: double.infinity, + padding: const EdgeInsets.symmetric( + horizontal: 10.0, + vertical: _inputVerticalPadding + 4, + ), + decoration: BoxDecoration( + color: colorScheme.surface, + borderRadius: BorderRadius.circular(4.0), + ), + child: SingleChildScrollView( + child: content.isEmpty + ? Text( + '(No content to preview)', + style: weightVariableTextStyle(context).merge( + const TextStyle( + fontSize: 16, + height: 22 / 16, + ), + ), + ) + : RichText( + text: _buildStyledText(content, context), + ), + ), + ); + } + + InlineSpan _buildStyledText(String text, BuildContext context) { + final boldPattern = RegExp(r'\*\*(.*?)\*\*'); + final italicPattern = RegExp(r'\*(.*?)\*'); + final codePattern = RegExp(r'```(.*?)```', dotAll: true); // Match ```code``` + + final normalStyle = weightVariableTextStyle(context).merge( + const TextStyle( + fontSize: 16, + height: 22 / 16, + ), + ); + final boldStyle = normalStyle.merge(const TextStyle(fontWeight: FontWeight.bold)); + final italicStyle = normalStyle.merge(const TextStyle(fontStyle: FontStyle.italic)); + + List spans = []; + int currentIndex = 0; + + // Process the text for code matches first + for (final match in codePattern.allMatches(text)) { + if (match.start > currentIndex) { + spans.add(TextSpan( + text: text.substring(currentIndex, match.start), + style: normalStyle, + )); + } + spans.add(WidgetSpan( + child: Container( + margin: const EdgeInsets.symmetric(vertical: 8.0), + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: const Color.fromARGB(156, 47, 47, 47), // Slightly darker gray for the background + borderRadius: BorderRadius.circular(6.0), + border: Border.all(color: Colors.grey[500]!), // Subtle border color + ), + child: Text( + match.group(1) ?? '', + style: const TextStyle( + fontFamily: 'Courier', // Monospace font for code + fontSize: 14, // Slightly smaller font for code + color: Colors.white, // Dark text color for good contrast + ), + ), + ), + )); + currentIndex = match.end; + } + + // Handle remaining text + String remainingText = text.substring(currentIndex); + currentIndex = 0; + + // Process bold text + for (final match in boldPattern.allMatches(remainingText)) { + if (match.start > currentIndex) { + spans.add(TextSpan( + text: remainingText.substring(currentIndex, match.start), + style: normalStyle, + )); + } + spans.add(TextSpan( + text: match.group(1), + style: boldStyle, + )); + currentIndex = match.end; + } + + // Process italic text + remainingText = remainingText.substring(currentIndex); + currentIndex = 0; + + for (final match in italicPattern.allMatches(remainingText)) { + if (match.start > currentIndex) { + spans.add(TextSpan( + text: remainingText.substring(currentIndex, match.start), + style: normalStyle, + )); + } + spans.add(TextSpan( + text: match.group(1), + style: italicStyle, + )); + currentIndex = match.end; + } + + // Add any remaining unstyled text + if (currentIndex < remainingText.length) { + spans.add(TextSpan( + text: remainingText.substring(currentIndex), + style: normalStyle, + )); + } + + return TextSpan(children: spans); + } +} abstract class ComposeBoxController extends State { ComposeTopicController? get topicController; ComposeContentController get contentController; From 00dbf4cbb38eca340b37245e0a56031a48ed975c Mon Sep 17 00:00:00 2001 From: divyanshu Date: Thu, 14 Nov 2024 14:32:47 +0530 Subject: [PATCH 3/3] changes --- lib/widgets/compose_box.dart | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/widgets/compose_box.dart b/lib/widgets/compose_box.dart index 0f6ee483a4..9f66dfb0da 100644 --- a/lib/widgets/compose_box.dart +++ b/lib/widgets/compose_box.dart @@ -1140,7 +1140,7 @@ class PreviewBox extends StatelessWidget { InlineSpan _buildStyledText(String text, BuildContext context) { final boldPattern = RegExp(r'\*\*(.*?)\*\*'); final italicPattern = RegExp(r'\*(.*?)\*'); - final codePattern = RegExp(r'```(.*?)```', dotAll: true); // Match ```code``` + final codePattern = RegExp(r'```(.*?)```', dotAll: true); final normalStyle = weightVariableTextStyle(context).merge( const TextStyle( @@ -1154,7 +1154,6 @@ class PreviewBox extends StatelessWidget { List spans = []; int currentIndex = 0; - // Process the text for code matches first for (final match in codePattern.allMatches(text)) { if (match.start > currentIndex) { spans.add(TextSpan( @@ -1167,16 +1166,16 @@ class PreviewBox extends StatelessWidget { margin: const EdgeInsets.symmetric(vertical: 8.0), padding: const EdgeInsets.all(10.0), decoration: BoxDecoration( - color: const Color.fromARGB(156, 47, 47, 47), // Slightly darker gray for the background + color: const Color.fromARGB(156, 47, 47, 47), borderRadius: BorderRadius.circular(6.0), - border: Border.all(color: Colors.grey[500]!), // Subtle border color + border: Border.all(color: Colors.grey[500]!), ), child: Text( match.group(1) ?? '', style: const TextStyle( - fontFamily: 'Courier', // Monospace font for code - fontSize: 14, // Slightly smaller font for code - color: Colors.white, // Dark text color for good contrast + fontFamily: 'Courier', + fontSize: 14, + color: Colors.white, ), ), ),