feat(App): update Finlytic Flutter app UI and blocs
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:finlytic_app/features/news/repositories/news_repository.dart';
|
||||
import 'news_event.dart';
|
||||
import 'news_state.dart';
|
||||
|
||||
class NewsBloc extends Bloc<NewsEvent, NewsState> {
|
||||
final NewsRepository repository;
|
||||
static const int pageSize = 20;
|
||||
StreamSubscription? _liveNewsSubscription;
|
||||
|
||||
NewsBloc({required this.repository}) : super(NewsInitial()) {
|
||||
on<FetchNews>(_onFetchNews);
|
||||
on<LoadMoreNews>(_onLoadMoreNews);
|
||||
on<ReceiveLiveNews>(_onReceiveLiveNews);
|
||||
|
||||
// Subscribe to live news from SignalR
|
||||
_liveNewsSubscription = repository.liveNewsStream.listen((article) {
|
||||
add(ReceiveLiveNews(article));
|
||||
});
|
||||
|
||||
// Connect to SignalR
|
||||
repository.connectToLiveFeed();
|
||||
}
|
||||
|
||||
Future<void> _onFetchNews(FetchNews event, Emitter<NewsState> emit) async {
|
||||
try {
|
||||
emit(NewsLoading());
|
||||
final articles = await repository.fetchNews(
|
||||
page: 1,
|
||||
pageSize: pageSize,
|
||||
symbol: event.symbol,
|
||||
isin: event.isin,
|
||||
date: event.date,
|
||||
);
|
||||
|
||||
emit(NewsLoaded(
|
||||
articles: articles,
|
||||
hasReachedMax: articles.length < pageSize,
|
||||
currentPage: 1,
|
||||
symbol: event.symbol,
|
||||
isin: event.isin,
|
||||
date: event.date,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(NewsError("Failed to fetch news. Please try again."));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLoadMoreNews(LoadMoreNews event, Emitter<NewsState> emit) async {
|
||||
if (state is NewsLoaded) {
|
||||
final currentState = state as NewsLoaded;
|
||||
if (currentState.hasReachedMax) return;
|
||||
|
||||
try {
|
||||
final nextPage = currentState.currentPage + 1;
|
||||
final articles = await repository.fetchNews(
|
||||
page: nextPage,
|
||||
pageSize: pageSize,
|
||||
symbol: currentState.symbol,
|
||||
isin: currentState.isin,
|
||||
date: currentState.date,
|
||||
);
|
||||
|
||||
if (articles.isEmpty) {
|
||||
emit(currentState.copyWith(hasReachedMax: true));
|
||||
} else {
|
||||
emit(NewsLoaded(
|
||||
articles: currentState.articles + articles,
|
||||
hasReachedMax: articles.length < pageSize,
|
||||
currentPage: nextPage,
|
||||
symbol: currentState.symbol,
|
||||
isin: currentState.isin,
|
||||
date: currentState.date,
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(NewsError("Failed to load more news."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onReceiveLiveNews(ReceiveLiveNews event, Emitter<NewsState> emit) {
|
||||
if (state is NewsLoaded) {
|
||||
final currentState = state as NewsLoaded;
|
||||
|
||||
// Check if the article matches current filters before prepending
|
||||
bool matchesFilter = true;
|
||||
if (currentState.symbol != null || currentState.isin != null) {
|
||||
// Needs proper matching logic if needed, but for now we prepend if there's no filter or if it matches
|
||||
// Assuming we prepend it regardless for live feed, or we could filter based on MatchedAssets if added to model.
|
||||
}
|
||||
|
||||
if (matchesFilter) {
|
||||
// Prepend new article to the top of the list!
|
||||
final updatedArticles = [event.article, ...currentState.articles];
|
||||
emit(currentState.copyWith(articles: updatedArticles));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_liveNewsSubscription?.cancel();
|
||||
repository.dispose();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user