Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SearchSnippet][Part-3] TF-3236 SearchSnippet search result and search suggest #3255

Open
wants to merge 12 commits into
base: feature/search-snippet
Choose a base branch
from

Conversation

tddang-linagora
Copy link
Contributor

@tddang-linagora tddang-linagora commented Nov 5, 2024

Issue

Demo

Search results

Web

Screen.Recording.2024-11-05.at.15.46.09.mov

Mobile

search_result.webm

Search suggest

Web

Screen.Recording.2024-11-06.at.11.53.01.mov

Mobile

mobile_suggest.webm

Copy link

github-actions bot commented Nov 5, 2024

This PR has been deployed to https://linagora.github.io/tmail-flutter/3255.

@dab246
Copy link
Member

dab246 commented Nov 5, 2024

Use below code to center highlight to frame

class HighlightTextWidget2 extends StatefulWidget {
  final String text;
  final String highlight;

  HighlightTextWidget2({required this.text, required this.highlight});

  @override
  _HighlightTextWidget2State createState() => _HighlightTextWidget2State();
}

class _HighlightTextWidget2State extends State<HighlightTextWidget2> {
  final ScrollController _scrollController = ScrollController();
  late List<TextSpan> _highlightedTextSpans;
  List<double> _highlightPositions = [];
  int _currentHighlightIndex = 0;
  late double _lastKnownWidth;

  @override
  void initState() {
    super.initState();
    _highlightedTextSpans = _buildHighlightedTextSpans();
    _lastKnownWidth = 0.0;
    WidgetsBinding.instance
        .addPostFrameCallback((_) => _calculateHighlightPositions());
  }

  // Tạo TextSpans với từ khóa highlight
  List<TextSpan> _buildHighlightedTextSpans() {
    List<TextSpan> spans = [];
    int start = 0;
    String lowerText = widget.text.toLowerCase();
    String lowerHighlight = widget.highlight.toLowerCase();

    int index = lowerText.indexOf(lowerHighlight);
    while (index != -1) {
      if (index > start) {
        spans.add(TextSpan(text: widget.text.substring(start, index)));
      }

      spans.add(TextSpan(
        text: widget.text.substring(index, index + widget.highlight.length),
        style: TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
      ));

      start = index + widget.highlight.length;
      index = lowerText.indexOf(lowerHighlight, start);
    }

    if (start < widget.text.length) {
      spans.add(TextSpan(text: widget.text.substring(start)));
    }

    return spans;
  }

  // Tính toán vị trí của tất cả các từ khóa highlight
  void _calculateHighlightPositions() {
    String lowerText = widget.text.toLowerCase();
    String lowerHighlight = widget.highlight.toLowerCase();
    _highlightPositions.clear();

    int index = lowerText.indexOf(lowerHighlight);
    while (index != -1) {
      final textBeforeHighlight = widget.text.substring(0, index);
      final TextPainter textPainter = TextPainter(
        text:
            TextSpan(text: textBeforeHighlight, style: TextStyle(fontSize: 16)),
        textDirection: TextDirection.ltr,
      )..layout();

      _highlightPositions.add(textPainter.width);
      index = lowerText.indexOf(lowerHighlight, index + lowerHighlight.length);
    }
  }

  // Cuộn để từ khóa hiện tại hiển thị ở giữa
  void _scrollToCurrentHighlight() {
    if (_highlightPositions.isNotEmpty) {
      final centerPosition = _highlightPositions[_currentHighlightIndex] -
          (MediaQuery.of(context).size.width / 2) +
          (widget.highlight.length * 8.0 / 2);

      _scrollController.animateTo(
        centerPosition.clamp(0.0, _scrollController.position.maxScrollExtent),
        duration: Duration(seconds: 1),
        curve: Curves.easeInOut,
      );
    }
  }

  // Xử lý khi chuyển sang highlight tiếp theo
  void _scrollToNextHighlight() {
    setState(() {
      _currentHighlightIndex =
          (_currentHighlightIndex + 1) % _highlightPositions.length;
    });
    _scrollToCurrentHighlight();
  }

  // Xử lý khi màn hình thay đổi kích thước
  void _onScreenResize(BoxConstraints constraints) {
    if (constraints.maxWidth != _lastKnownWidth) {
      _lastKnownWidth = constraints.maxWidth;
      _scrollToCurrentHighlight();
    }
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // Gọi hàm _onScreenResize để kiểm tra khi màn hình thay đổi kích thước
        WidgetsBinding.instance.addPostFrameCallback((_) {
          _onScreenResize(constraints);
        });

        return Column(
          children: [
            ElevatedButton(
              onPressed: _scrollToNextHighlight,
              child: Text("Scroll to Next Highlight"),
            ),
            SizedBox(
              height: 60,
              child: ListView(
                scrollDirection: Axis.horizontal,
                controller: _scrollController,
                physics:
                    NeverScrollableScrollPhysics(), // Vô hiệu hóa cuộn ngang của người dùng
                children: [
                  RichText(
                    text: TextSpan(
                      style: TextStyle(color: Colors.black, fontSize: 16),
                      children: _highlightedTextSpans,
                    ),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                  ),
                ],
              ),
            ),
          ],
        );
      },
    );
  }
}

@tddang-linagora tddang-linagora changed the title TF-3236 SearchSnippet search result [SearchSnippet][Part-3] TF-3236 SearchSnippet search result Nov 5, 2024
@tddang-linagora tddang-linagora changed the title [SearchSnippet][Part-3] TF-3236 SearchSnippet search result [SearchSnippet][Part-3] TF-3236 SearchSnippet search result and search suggest Nov 6, 2024
@tddang-linagora tddang-linagora force-pushed the feature/TF-3236-search-snippet-web-search-result branch from 6f785cc to 0ceb832 Compare November 6, 2024 05:05
@tddang-linagora
Copy link
Contributor Author

Should not scroll when don't have hightlight text

Please check again

@tddang-linagora
Copy link
Contributor Author

Bottom loading bar auto show when perform search email -> click open email -> go back on web responsive mobile

Screen.Recording.2024-11-15.at.11.27.29.mov

@dab246
Copy link
Member

dab246 commented Nov 15, 2024

  • perform load more fast still not show highlight text
Screenshot 2024-11-15 at 12 09 40

@dab246
Copy link
Member

dab246 commented Nov 18, 2024

  • Email list automatically scrolls to top when opening second email
Screen.Recording.2024-11-18.at.10.24.31.mov

@tddang-linagora
Copy link
Contributor Author

Email list automatically scrolls to top when opening second email

Screen.Recording.2024-11-18.at.14.30.32.mov

@@ -80,7 +83,13 @@ class _RichTextBuilderState extends State<RichTextBuilder> with AutomaticKeepAli
return SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
child: text,
child: VisibilityDetector(
key: ValueKey(hashCode),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it dangerous to use hashcode like that? What if 2 different objects have the same hashcode? If the RichTextBuilder object changes frequently it can lead to unnecessary widget re-rendering.

@dab246
Copy link
Member

dab246 commented Nov 18, 2024

  • perform load more fast still not show highlight text
  • This error appears again.
Screen.Recording.2024-11-18.at.18.52.50.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants