Jisho-Study-Tool/lib/components/search/search_results_body/search_card.dart

157 lines
5.0 KiB
Dart
Raw Normal View History

2020-07-09 22:17:10 +02:00
import 'package:flutter/material.dart';
import 'package:jisho_study_tool/services/kanji_regex.dart';
2020-07-09 22:17:10 +02:00
import 'package:unofficial_jisho_api/api.dart';
import './parts/common_badge.dart';
2021-03-02 23:09:56 +01:00
import './parts/header.dart';
import './parts/jlpt_badge.dart';
2021-03-02 23:09:56 +01:00
import './parts/other_forms.dart';
import './parts/senses.dart';
import './parts/wanikani_badge.dart';
import '../../../settings.dart';
import 'parts/audio_player.dart';
import 'parts/kanji.dart';
import 'parts/links.dart';
import 'parts/notes.dart';
2020-08-23 00:38:42 +02:00
class SearchResultCard extends StatefulWidget {
2021-03-03 00:24:25 +01:00
final JishoResult result;
2021-07-26 21:39:17 +02:00
late final JishoJapaneseWord mainWord;
late final List<JishoJapaneseWord> otherForms;
2020-08-23 00:06:09 +02:00
2021-12-01 23:09:53 +01:00
SearchResultCard({
required this.result,
Key? key,
}) : mainWord = result.japanese[0],
otherForms = result.japanese.sublist(1),
super(key: key);
2020-08-23 00:06:09 +02:00
@override
_SearchResultCardState createState() => _SearchResultCardState();
}
class _SearchResultCardState extends State<SearchResultCard> {
PhrasePageScrapeResultData? extraData;
2022-05-01 23:34:11 +02:00
bool? extraDataSearchFailed;
Future<PhrasePageScrapeResult?> _scrape(JishoResult result) =>
(!(result.japanese[0].word == null && result.japanese[0].reading == null))
? scrapeForPhrase(
widget.result.japanese[0].word ??
widget.result.japanese[0].reading!,
)
: Future(() => null);
List<JishoSenseLink> get links =>
[for (final sense in widget.result.senses) ...sense.links];
bool get hasAttribution =>
widget.result.attribution.jmdict ||
widget.result.attribution.jmnedict ||
(widget.result.attribution.dbpedia != null);
String? get jlptLevel {
if (widget.result.jlpt.isEmpty) return null;
final jlpt = List.from(widget.result.jlpt);
jlpt.sort();
return jlpt.last;
}
List<String> get kanji => kanjiRegex
.allMatches(
widget.result.japanese
.map((w) => '${w.word ?? ""}${w.reading ?? ""}')
.join(),
)
.map((match) => match.group(0)!)
.toSet()
.toList();
Widget get _header => IntrinsicWidth(
2021-04-11 01:48:43 +02:00
child: Row(
2021-12-01 23:09:53 +01:00
mainAxisAlignment: MainAxisAlignment.spaceBetween,
2021-04-11 01:48:43 +02:00
children: [
JapaneseHeader(word: widget.mainWord),
2021-04-11 01:48:43 +02:00
Row(
children: [
2021-12-01 23:09:53 +01:00
WKBadge(
level: widget.result.tags.firstWhere(
2021-12-01 23:09:53 +01:00
(tag) => tag.contains('wanikani'),
orElse: () => '',
),
),
JLPTBadge(jlptLevel: jlptLevel),
CommonBadge(isCommon: widget.result.isCommon ?? false)
2021-04-11 01:48:43 +02:00
],
)
],
),
);
static const _margin = SizedBox(height: 20);
List<Widget> _withMargin(Widget w) => [_margin, w];
Widget _body({PhrasePageScrapeResultData? extendedData}) => Container(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (extendedData != null && extendedData.audio.isNotEmpty) ...[
// TODO: There's usually multiple mimetypes in the data.
// If one mimetype fails, the app should try to use another one.
AudioPlayer(audio: extendedData.audio.first),
const SizedBox(height: 10),
2021-04-11 01:48:43 +02:00
],
Senses(
senses: widget.result.senses,
extraData: extendedData?.meanings,
),
if (widget.otherForms.isNotEmpty)
..._withMargin(OtherForms(forms: widget.otherForms)),
if (extendedData != null && extendedData.notes.isNotEmpty)
..._withMargin(Notes(notes: extendedData.notes)),
if (kanji.isNotEmpty) ..._withMargin(KanjiRow(kanji: kanji)),
if (links.isNotEmpty || hasAttribution)
..._withMargin(
Links(
links: links,
attribution: widget.result.attribution,
),
)
],
),
);
@override
Widget build(BuildContext context) {
final backgroundColor = Theme.of(context).scaffoldBackgroundColor;
return ExpansionTile(
collapsedBackgroundColor: backgroundColor,
backgroundColor: backgroundColor,
onExpansionChanged: (b) async {
if (extensiveSearchEnabled && extraData == null) {
final data = await _scrape(widget.result);
setState(() {
2022-05-01 23:34:11 +02:00
extraDataSearchFailed = !(data?.found ?? false);
extraData = !extraDataSearchFailed! ? data!.data : null;
});
}
},
title: _header,
children: [
2022-05-01 23:34:11 +02:00
if (extensiveSearchEnabled && extraDataSearchFailed == null)
const Padding(
padding: EdgeInsets.symmetric(vertical: 10),
child: Center(child: CircularProgressIndicator()),
)
2022-05-01 23:34:11 +02:00
else if (!extraDataSearchFailed!)
_body(extendedData: extraData)
else
_body()
2020-08-24 23:45:47 +02:00
],
2020-08-23 00:06:09 +02:00
);
}
}