107 lines
3.3 KiB
Dart
107 lines
3.3 KiB
Dart
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 pushed over the central SignalRService's
|
|
// `/hubs/news` connection (authenticated, managed in main.dart).
|
|
_liveNewsSubscription = repository.liveNewsStream.listen((article) {
|
|
add(ReceiveLiveNews(article));
|
|
});
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|