chore: maintain state of filtering / search when navigating between vocab list and vocab details (#2316)

This commit is contained in:
ggurdin 2025-04-02 09:19:04 -04:00 committed by GitHub
parent 8b9fce8e56
commit f1106e0aa8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 56 additions and 69 deletions

View file

@ -13,6 +13,7 @@ import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dar
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
import 'package:fluffychat/pangea/morphs/default_morph_mapping.dart';
import 'package:fluffychat/pangea/morphs/morph_models.dart';
import 'package:fluffychat/pangea/morphs/morph_repo.dart';
@ -36,17 +37,28 @@ class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
ConstructIdentifier? localConstructZoom;
ConstructTypeEnum localView = ConstructTypeEnum.vocab;
// @ggurdin
//TODO: make language-specific
MorphFeaturesAndTags morphs = defaultMorphMapping;
List<MorphFeature> features = defaultMorphMapping.displayFeatures;
bool isSearching = false;
final TextEditingController searchController = TextEditingController();
ConstructLevelEnum? selectedConstructLevel;
@override
void initState() {
super.initState();
localView = widget.view;
_setConstructZoom(widget.constructZoom);
setConstructZoom(widget.constructZoom);
_setMorphs();
searchController.addListener(() {
if (mounted) setState(() {});
});
}
@override
void dispose() {
searchController.dispose();
super.dispose();
}
Future<void> _setMorphs() async {
@ -70,7 +82,7 @@ class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
}
}
void _setConstructZoom(ConstructIdentifier? id) {
void setConstructZoom(ConstructIdentifier? id) {
if (id != null && id.type != localView) {
localView = id.type;
}
@ -78,6 +90,20 @@ class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
setState(() => {});
}
void setSelectedConstructLevel(ConstructLevelEnum level) {
setState(() {
selectedConstructLevel = selectedConstructLevel == level ? null : level;
});
}
void toggleSearching() {
setState(() {
isSearching = !isSearching;
selectedConstructLevel = null;
searchController.clear();
});
}
@override
Widget build(BuildContext context) {
return FullWidthDialog(
@ -96,7 +122,7 @@ class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
: const Icon(Icons.arrow_back),
onPressed: localConstructZoom == null
? () => Navigator.of(context).pop()
: () => _setConstructZoom(null),
: () => setConstructZoom(null),
),
actions: [
TextButton.icon(
@ -137,13 +163,10 @@ class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
),
body: localView == ConstructTypeEnum.morph
? localConstructZoom == null
? MorphAnalyticsListView(
onConstructZoom: _setConstructZoom,
controller: this,
)
? MorphAnalyticsListView(controller: this)
: MorphDetailsView(constructId: localConstructZoom!)
: localConstructZoom == null
? VocabAnalyticsListView(onConstructZoom: _setConstructZoom)
? VocabAnalyticsListView(controller: this)
: VocabDetailsView(constructId: localConstructZoom!),
),
maxWidth: 600,

View file

@ -17,11 +17,9 @@ import 'package:fluffychat/pangea/user/client_extension.dart';
import 'package:fluffychat/widgets/matrix.dart';
class MorphAnalyticsListView extends StatelessWidget {
final void Function(ConstructIdentifier) onConstructZoom;
final AnalyticsPopupWrapperState controller;
const MorphAnalyticsListView({
required this.onConstructZoom,
required this.controller,
super.key,
});
@ -51,7 +49,7 @@ class MorphAnalyticsListView extends StatelessWidget {
.getDisplayTags(feature.feature)
.map((tag) => tag.toLowerCase())
.toSet(),
onConstructZoom: onConstructZoom,
onConstructZoom: controller.setConstructZoom,
)
: const SizedBox.shrink();
},

View file

@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_tile.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
@ -13,51 +13,14 @@ import 'package:fluffychat/widgets/matrix.dart';
/// Displays vocab analytics, sorted into categories
/// (flowers, greens, and seeds) by points
class VocabAnalyticsListView extends StatefulWidget {
final void Function(ConstructIdentifier) onConstructZoom;
class VocabAnalyticsListView extends StatelessWidget {
final AnalyticsPopupWrapperState controller;
const VocabAnalyticsListView({
super.key,
required this.onConstructZoom,
required this.controller,
});
@override
VocabAnalyticsListViewState createState() => VocabAnalyticsListViewState();
}
class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
bool _isSearching = false;
final TextEditingController _searchController = TextEditingController();
ConstructLevelEnum? _selectedConstructLevel;
@override
void initState() {
super.initState();
_searchController.addListener(() {
if (mounted) setState(() {});
});
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
void _setSelectedConstructLevel(ConstructLevelEnum level) {
setState(() {
_selectedConstructLevel = _selectedConstructLevel == level ? null : level;
});
}
void _toggleSearching() {
setState(() {
_isSearching = !_isSearching;
_selectedConstructLevel = null;
_searchController.clear();
});
}
List<ConstructUses> get _vocab => MatrixState
.pangeaController.getAnalytics.constructListModel
.constructList(type: ConstructTypeEnum.vocab)
@ -67,13 +30,13 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
.where(
(use) =>
use.lemma.isNotEmpty &&
(_selectedConstructLevel == null
(controller.selectedConstructLevel == null
? true
: use.lemmaCategory == _selectedConstructLevel) &&
(_isSearching
: use.lemmaCategory == controller.selectedConstructLevel) &&
(controller.isSearching
? use.lemma
.toLowerCase()
.contains(_searchController.text.toLowerCase())
.contains(controller.searchController.text.toLowerCase())
: true),
)
.toList();
@ -86,14 +49,16 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
.where((e) => e.lemmaCategory == constructLevelCategory)
.length;
return InkWell(
onTap: () => _setSelectedConstructLevel(constructLevelCategory),
onTap: () =>
controller.setSelectedConstructLevel(constructLevelCategory),
customBorder: const CircleBorder(),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _selectedConstructLevel == constructLevelCategory
? constructLevelCategory.color(context).withAlpha(50)
: null,
color:
controller.selectedConstructLevel == constructLevelCategory
? constructLevelCategory.color(context).withAlpha(50)
: null,
),
padding: const EdgeInsets.all(8.0),
child: Badge(
@ -109,7 +74,7 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
filters.add(
IconButton(
icon: const Icon(Icons.search_outlined),
onPressed: _toggleSearching,
onPressed: controller.toggleSearching,
),
);
@ -123,8 +88,9 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
padding:
EdgeInsets.symmetric(horizontal: _isSearching ? 8.0 : 24.0),
padding: EdgeInsets.symmetric(
horizontal: controller.isSearching ? 8.0 : 24.0,
),
child: Container(
height: 60,
alignment: Alignment.center,
@ -139,7 +105,7 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
opacity: animation,
child: child,
),
child: _isSearching
child: controller.isSearching
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
key: const ValueKey('search'),
@ -147,7 +113,7 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
Expanded(
child: TextField(
autofocus: true,
controller: _searchController,
controller: controller.searchController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 6.0,
@ -160,7 +126,7 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
),
IconButton(
icon: const Icon(Icons.close),
onPressed: _toggleSearching,
onPressed: controller.toggleSearching,
),
],
)
@ -189,7 +155,7 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
itemBuilder: (context, index) {
final vocabItem = _filteredVocab[index];
return VocabAnalyticsListTile(
onTap: () => widget.onConstructZoom(vocabItem.id),
onTap: () => controller.setConstructZoom(vocabItem.id),
constructUse: vocabItem,
);
},