@@ -7,78 +7,195 @@ import 'package:waves/features/explore/repository/explore_repository.dart';
77import 'package:waves/features/settings/repository/settings_repository.dart' ;
88
99class ExploreController extends ChangeNotifier {
10+ ExploreController ()
11+ : threadType = getIt <SettingsRepository >().readDefaultThread () {
12+ _applyCachedState ();
13+ if (! _tagCache.containsKey (threadType)) {
14+ _loadTags ();
15+ }
16+ }
17+
1018 final ExploreRepository _repository = getIt <ExploreRepository >();
1119
20+ static final Map <ThreadFeedType , _CachedExploreResult <TrendingTagModel >>
21+ _tagCache = {};
22+ static final Map <ThreadFeedType , _CachedExploreResult <TrendingAuthorModel >>
23+ _authorCache = {};
24+
1225 ThreadFeedType threadType;
1326 ViewState tagsState = ViewState .loading;
1427 ViewState authorsState = ViewState .loading;
1528
16- List <TrendingTagModel > tags = [];
17- List <TrendingAuthorModel > authors = [];
29+ List <TrendingTagModel > tags = const < TrendingTagModel > [];
30+ List <TrendingAuthorModel > authors = const < TrendingAuthorModel > [];
1831
19- bool _authorsLoaded = false ;
32+ bool _isLoadingTags = false ;
33+ bool _isLoadingAuthors = false ;
2034
21- ExploreController ()
22- : threadType = getIt <SettingsRepository >().readDefaultThread () {
23- _loadTags ();
35+ void _applyCachedState () {
36+ final cachedTags = _tagCache[threadType];
37+ if (cachedTags != null ) {
38+ tags = cachedTags.data;
39+ tagsState = cachedTags.state;
40+ } else {
41+ tags = const < TrendingTagModel > [];
42+ tagsState = ViewState .loading;
43+ }
44+
45+ final cachedAuthors = _authorCache[threadType];
46+ if (cachedAuthors != null ) {
47+ authors = cachedAuthors.data;
48+ authorsState = cachedAuthors.state;
49+ } else {
50+ authors = const < TrendingAuthorModel > [];
51+ authorsState = ViewState .loading;
52+ }
2453 }
2554
26- Future <void > _loadTags () async {
27- tagsState = ViewState .loading;
28- notifyListeners ();
55+ Future <void > _loadTags ({bool forceRefresh = false }) async {
56+ if (_isLoadingTags) return ;
2957
30- final container = _getContainer ();
31- final tagRes = await _repository.getTrendingTags (container);
32- if (tagRes.isSuccess && tagRes.data != null && tagRes.data! .isNotEmpty) {
33- tags = tagRes.data! ;
34- tagsState = ViewState .data;
58+ final currentType = threadType;
59+ if (! forceRefresh) {
60+ final cached = _tagCache[currentType];
61+ if (cached != null ) {
62+ final previousState = tagsState;
63+ final previousTags = tags;
64+ tags = cached.data;
65+ tagsState = cached.state;
66+ if (! identical (previousTags, tags) || previousState != tagsState) {
67+ notifyListeners ();
68+ }
69+ return ;
70+ }
3571 } else {
36- tags = [];
37- tagsState = ViewState .error;
72+ _tagCache.remove (currentType);
3873 }
74+
75+ _isLoadingTags = true ;
76+ tagsState = ViewState .loading;
3977 notifyListeners ();
78+
79+ try {
80+ final container = _getContainer (currentType);
81+ final tagRes = await _repository.getTrendingTags (container);
82+
83+ if (threadType != currentType) {
84+ return ;
85+ }
86+
87+ if (tagRes.isSuccess && tagRes.data != null ) {
88+ final list = tagRes.data! ;
89+ if (list.isEmpty) {
90+ tags = const < TrendingTagModel > [];
91+ tagsState = ViewState .empty;
92+ _tagCache[currentType] = _CachedExploreResult <TrendingTagModel >(
93+ data: const < TrendingTagModel > [],
94+ state: ViewState .empty,
95+ );
96+ } else {
97+ final immutableList =
98+ List <TrendingTagModel >.unmodifiable (List .of (list));
99+ tags = immutableList;
100+ tagsState = ViewState .data;
101+ _tagCache[currentType] = _CachedExploreResult <TrendingTagModel >(
102+ data: immutableList,
103+ state: ViewState .data,
104+ );
105+ }
106+ } else {
107+ tags = const < TrendingTagModel > [];
108+ tagsState = ViewState .error;
109+ }
110+ } finally {
111+ _isLoadingTags = false ;
112+ if (threadType == currentType) {
113+ notifyListeners ();
114+ }
115+ }
40116 }
41117
42- Future <void > loadAuthorsIfNeeded () async {
43- if (_authorsLoaded) {
44- return ;
118+ Future <void > loadAuthorsIfNeeded ({bool forceRefresh = false }) async {
119+ if (_isLoadingAuthors) return ;
120+
121+ final currentType = threadType;
122+ if (! forceRefresh) {
123+ final cached = _authorCache[currentType];
124+ if (cached != null ) {
125+ final previousState = authorsState;
126+ final previousAuthors = authors;
127+ authors = cached.data;
128+ authorsState = cached.state;
129+ if (! identical (previousAuthors, authors) ||
130+ previousState != authorsState) {
131+ notifyListeners ();
132+ }
133+ return ;
134+ }
135+ } else {
136+ _authorCache.remove (currentType);
45137 }
46138
139+ _isLoadingAuthors = true ;
47140 authorsState = ViewState .loading;
48141 notifyListeners ();
49142
50- final container = _getContainer ();
51- final authorRes = await _repository.getTrendingAuthors (container);
52- if (authorRes.isSuccess && authorRes.data != null &&
53- authorRes.data! .isNotEmpty) {
54- authors = authorRes.data! ;
55- authorsState = ViewState .data;
56- _authorsLoaded = true ;
57- } else {
58- authors = [];
59- authorsState = ViewState .error;
60- _authorsLoaded = false ;
143+ try {
144+ final container = _getContainer (currentType);
145+ final authorRes = await _repository.getTrendingAuthors (container);
146+
147+ if (threadType != currentType) {
148+ return ;
149+ }
150+
151+ if (authorRes.isSuccess && authorRes.data != null ) {
152+ final list = authorRes.data! ;
153+ if (list.isEmpty) {
154+ authors = const < TrendingAuthorModel > [];
155+ authorsState = ViewState .empty;
156+ _authorCache[currentType] =
157+ const _CachedExploreResult <TrendingAuthorModel >(
158+ data: < TrendingAuthorModel > [],
159+ state: ViewState .empty,
160+ );
161+ } else {
162+ final immutableList =
163+ List <TrendingAuthorModel >.unmodifiable (List .of (list));
164+ authors = immutableList;
165+ authorsState = ViewState .data;
166+ _authorCache[currentType] = _CachedExploreResult <TrendingAuthorModel >(
167+ data: immutableList,
168+ state: ViewState .data,
169+ );
170+ }
171+ } else {
172+ authors = const < TrendingAuthorModel > [];
173+ authorsState = ViewState .error;
174+ }
175+ } finally {
176+ _isLoadingAuthors = false ;
177+ if (threadType == currentType) {
178+ notifyListeners ();
179+ }
61180 }
62- notifyListeners ();
63181 }
64182
65183 void onChangeThreadType (ThreadFeedType type) {
66- if (threadType != type) {
67- threadType = type;
68- _resetAuthors ();
69- _loadTags ();
184+ if (threadType == type) {
185+ return ;
70186 }
71- }
72187
73- void _resetAuthors () {
74- authors = [];
75- authorsState = ViewState .loading;
76- _authorsLoaded = false ;
188+ threadType = type;
189+ _applyCachedState ();
77190 notifyListeners ();
191+
192+ if (! _tagCache.containsKey (threadType)) {
193+ _loadTags ();
194+ }
78195 }
79196
80- String _getContainer () {
81- switch (threadType ) {
197+ String _getContainer (ThreadFeedType type ) {
198+ switch (type ) {
82199 case ThreadFeedType .ecency:
83200 return 'ecency.waves' ;
84201 case ThreadFeedType .peakd:
@@ -92,3 +209,10 @@ class ExploreController extends ChangeNotifier {
92209 }
93210 }
94211}
212+
213+ class _CachedExploreResult <T > {
214+ const _CachedExploreResult ({required this .data, required this .state});
215+
216+ final List <T > data;
217+ final ViewState state;
218+ }
0 commit comments