searchForSentences method

Future<List<MassifResult>> searchForSentences(
  1. {required BuildContext context,
  2. required AppModel appModel,
  3. required String searchTerm}
)

Search the Massif API for example sentences and return a list of results.

Implementation

Future<List<MassifResult>> searchForSentences({
  required BuildContext context,
  required AppModel appModel,
  required String searchTerm,
}) async {
  if (searchTerm.trim().isEmpty) {
    return [];
  }

  if (_massifCache[searchTerm] != null) {
    return _massifCache[searchTerm]!;
  }

  List<MassifResult> results = [];

  late http.Response response;

  try {
    /// Query the Massif API for results.
    response = await _client.get(Uri.parse(
        'https://massif.la/ja/search?&fmt=json&q=${Uri.encodeComponent(searchTerm)}'));

    Map<String, dynamic> json = jsonDecode(utf8.decode(response.bodyBytes));

    /// For each response, create a [MassifResult] that can be used to display
    /// the widget as well as hold the sentence and source data.
    List<Map<String, dynamic>> queryResponses =
        List<Map<String, dynamic>>.from(json['results']);

    for (Map<String, dynamic> queryResponse in queryResponses) {
      Map<String, String> sampleSource =
          Map<String, String>.from(queryResponse['sample_source']);
      String source = sampleSource['title']!;
      String text = queryResponse['text'];

      List<InlineSpan> spans = [];

      String highlightedText = queryResponse['highlighted_html'];
      List<String> splitWithDelims =
          highlightedText.splitWithDelim(RegExp(r'<em>(.*?)<\/em>'));

      final buffer = StringBuffer();
      TextRange range = TextRange.empty;

      bool firstFound = false;
      bool consecutiveFlag = false;
      int? start;
      int? end;

      for (String splitWithDelim in splitWithDelims) {
        if (splitWithDelim.startsWith('<em>') &&
            splitWithDelim.endsWith('</em>')) {
          String text =
              splitWithDelim.replaceAll('<em>', '').replaceAll('</em>', '');

          if (!firstFound) {
            consecutiveFlag = true;
            firstFound = true;
            start = buffer.length;
            end = buffer.length + text.length;
          }

          if (firstFound && consecutiveFlag) {
            end = buffer.length + text.length;
          }

          buffer.write(text);

          spans.add(
            TextSpan(
              text: text,
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Theme.of(context).colorScheme.primary,
                fontSize: Theme.of(context).textTheme.titleMedium?.fontSize,
              ),
            ),
          );
        } else {
          if (splitWithDelim.trim().isNotEmpty) {
            consecutiveFlag = false;
          }

          buffer.write(splitWithDelim);
          spans.add(
            TextSpan(
              text: splitWithDelim,
              style: TextStyle(
                fontWeight: FontWeight.w500,
                fontSize: Theme.of(context).textTheme.titleMedium?.fontSize,
              ),
            ),
          );
        }
      }

      if (start != null && end != null) {
        range = TextRange(
          start: start,
          end: end,
        );
      }

      MassifResult result = MassifResult(
        text: text,
        range: range,
        source: source,
        spans: spans,
      );

      results.add(result);
    }

    /// Save this into cache.
    _massifCache[searchTerm] = results;

    return results;
  } catch (e) {
    /// Used to log if this third-party service is down or changes domains.
    appModel.showFailedToCommunicateMessage();
    throw Exception(
      'Failed to communicate with Massif: ${response.reasonPhrase}',
    );
  }
}