searchForSentences method

Future<List<ImmersionKitResult>> 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<ImmersionKitResult>> searchForSentences({
  required BuildContext context,
  required AppModel appModel,
  required String searchTerm,
}) async {
  if (searchTerm.trim().isEmpty) {
    return [];
  }

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

  List<ImmersionKitResult> results = [];

  late http.Response response;

  try {
    /// Query the ImmersionKit API for results.
    response = await _client.get(Uri.parse(
        'https://api.immersionkit.com/look_up_dictionary?keyword=${Uri.encodeComponent(searchTerm)}&sort=shortness&min_length=${max(searchTerm.length, 10)}'));

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

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

    Map<String, dynamic> firstHit = dataResponse.first;

    List<Map<String, dynamic>> examples =
        List<Map<String, dynamic>>.from(firstHit['examples']);

    if (examples.isEmpty) {
      response = await _client.get(Uri.parse(
          'https://api.immersionkit.com/look_up_dictionary?keyword=${Uri.encodeComponent(searchTerm)}'));
      json = jsonDecode(utf8.decode(response.bodyBytes));
      dataResponse = List<Map<String, dynamic>>.from(json['data']);
      firstHit = dataResponse.first;
      examples = List<Map<String, dynamic>>.from(firstHit['examples']);
    }

    for (Map<String, dynamic> example in examples) {
      String source = example['deck_name'];
      String text = example['sentence'];

      List<String> wordList = List<String>.from(example['word_list']);
      List<int> wordIndices = List<int>.from(example['word_index']);

      String imageUrl = example['image_url'];
      String audioUrl = example['sound_url'];

      ImmersionKitResult result = ImmersionKitResult(
        text: text,
        source: source,
        imageUrl: imageUrl,
        audioUrl: audioUrl,
        wordList: wordList,
        wordIndices: wordIndices,
      );

      /// Sentence examples that are merely the word itself are pretty
      /// redundant.
      if (result.text != searchTerm) {
        results.add(result);
      }
    }

    /// Make sure series aren't too consecutive.
    results.shuffle();

    /// Results with images come first.
    results.sort((a, b) {
      int hasImage = (a.imageUrl.isNotEmpty ? -1 : 1)
          .compareTo(b.imageUrl.isNotEmpty ? -1 : 1);

      if (hasImage != 0) {
        return hasImage;
      }

      int hasAudio = (a.audioUrl.isNotEmpty ? -1 : 1)
          .compareTo(b.audioUrl.isNotEmpty ? -1 : 1);

      if (hasAudio != 0) {
        return hasAudio;
      }

      return a.text.length.compareTo(b.text.length);
    });

    /// Save this into cache.
    _immersionKitCache[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 ImmersionKit: ${response.reasonPhrase}',
    );
  }
}