feat(App): update Finlytic Flutter app UI and blocs
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:finlytic_app/features/asset_detail/repositories/asset_repository.dart';
|
||||
import 'asset_detail_event.dart';
|
||||
import 'asset_detail_state.dart';
|
||||
|
||||
class AssetDetailBloc extends Bloc<AssetDetailEvent, AssetDetailState> {
|
||||
final AssetRepository repository;
|
||||
|
||||
AssetDetailBloc({required this.repository}) : super(AssetDetailInitial()) {
|
||||
on<LoadAssetData>(_onLoadAssetData);
|
||||
on<ForceRefreshAssetData>(_onForceRefreshAssetData);
|
||||
}
|
||||
|
||||
Future<void> _onLoadAssetData(LoadAssetData event, Emitter<AssetDetailState> emit) async {
|
||||
emit(AssetDetailLoading());
|
||||
try {
|
||||
final results = await Future.wait([
|
||||
repository.getFundamentalData(event.symbol),
|
||||
repository.getTechnicalAnalysis(event.symbol),
|
||||
]);
|
||||
|
||||
emit(AssetDetailLoaded(
|
||||
fundamentalData: results[0] as dynamic,
|
||||
technicalAnalysis: results[1] as dynamic,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(AssetDetailError("Fehler beim Laden der Asset-Daten."));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onForceRefreshAssetData(ForceRefreshAssetData event, Emitter<AssetDetailState> emit) async {
|
||||
try {
|
||||
await repository.forceRefreshFundamentalData(event.symbol);
|
||||
// Optional: re-load after a delay, or rely on MQTT/SignalR to push the new data.
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class AssetDetailEvent extends Equatable {
|
||||
const AssetDetailEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class LoadAssetData extends AssetDetailEvent {
|
||||
final String symbol;
|
||||
|
||||
const LoadAssetData(this.symbol);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [symbol];
|
||||
}
|
||||
|
||||
class ForceRefreshAssetData extends AssetDetailEvent {
|
||||
final String symbol;
|
||||
|
||||
const ForceRefreshAssetData(this.symbol);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [symbol];
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:finlytic_app/features/asset_detail/models/fundamental_data_model.dart';
|
||||
import 'package:finlytic_app/features/asset_detail/models/technical_analysis_model.dart';
|
||||
|
||||
abstract class AssetDetailState extends Equatable {
|
||||
const AssetDetailState();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class AssetDetailInitial extends AssetDetailState {}
|
||||
|
||||
class AssetDetailLoading extends AssetDetailState {}
|
||||
|
||||
class AssetDetailLoaded extends AssetDetailState {
|
||||
final FundamentalDataModel? fundamentalData;
|
||||
final TechnicalAnalysisModel? technicalAnalysis;
|
||||
|
||||
const AssetDetailLoaded({this.fundamentalData, this.technicalAnalysis});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [fundamentalData, technicalAnalysis];
|
||||
}
|
||||
|
||||
class AssetDetailError extends AssetDetailState {
|
||||
final String message;
|
||||
|
||||
const AssetDetailError(this.message);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'asset_fundamentals_event.dart';
|
||||
import 'asset_fundamentals_state.dart';
|
||||
import '../../repositories/asset_repository.dart';
|
||||
|
||||
class AssetFundamentalsBloc extends Bloc<AssetFundamentalsEvent, AssetFundamentalsState> {
|
||||
final AssetRepository repository;
|
||||
AssetFundamentalsBloc({required this.repository}) : super(AssetFundamentalsInitial()) {
|
||||
on<LoadAssetFundamentals>((event, emit) async {
|
||||
emit(AssetFundamentalsLoading());
|
||||
try {
|
||||
final data = await repository.getAssetFundamentals(event.isin, event.forceRefresh, ticker: event.ticker);
|
||||
emit(AssetFundamentalsLoaded(data));
|
||||
} catch (e) {
|
||||
emit(AssetFundamentalsError(e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
abstract class AssetFundamentalsEvent {}
|
||||
class LoadAssetFundamentals extends AssetFundamentalsEvent {
|
||||
final String isin;
|
||||
final bool forceRefresh;
|
||||
final String? ticker;
|
||||
LoadAssetFundamentals(this.isin, {this.forceRefresh = false, this.ticker});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import '../../models/fundamental_data_model.dart';
|
||||
|
||||
abstract class AssetFundamentalsState {}
|
||||
class AssetFundamentalsInitial extends AssetFundamentalsState {}
|
||||
class AssetFundamentalsLoading extends AssetFundamentalsState {}
|
||||
class AssetFundamentalsLoaded extends AssetFundamentalsState {
|
||||
final FundamentalDataModel? data;
|
||||
AssetFundamentalsLoaded(this.data);
|
||||
}
|
||||
class AssetFundamentalsError extends AssetFundamentalsState {
|
||||
final String message;
|
||||
AssetFundamentalsError(this.message);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'asset_header_event.dart';
|
||||
import 'asset_header_state.dart';
|
||||
import '../../repositories/asset_repository.dart';
|
||||
|
||||
class AssetHeaderBloc extends Bloc<AssetHeaderEvent, AssetHeaderState> {
|
||||
final AssetRepository repository;
|
||||
AssetHeaderBloc({required this.repository}) : super(AssetHeaderInitial()) {
|
||||
on<LoadAssetHeader>((event, emit) async {
|
||||
final prevData = state is AssetHeaderLoaded ? (state as AssetHeaderLoaded).data : (state is AssetHeaderLoading ? (state as AssetHeaderLoading).previousData : null);
|
||||
emit(AssetHeaderLoading(previousData: prevData));
|
||||
try {
|
||||
final data = await repository.getAssetHeader(event.isin, exchange: event.exchange, ticker: event.ticker);
|
||||
emit(AssetHeaderLoaded(data));
|
||||
} catch (e) {
|
||||
emit(AssetHeaderError(e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
abstract class AssetHeaderEvent {}
|
||||
class LoadAssetHeader extends AssetHeaderEvent {
|
||||
final String isin;
|
||||
final bool forceRefresh;
|
||||
final String? exchange;
|
||||
final String? ticker;
|
||||
LoadAssetHeader(this.isin, {this.forceRefresh = false, this.exchange, this.ticker});
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import '../../models/asset_model.dart';
|
||||
|
||||
abstract class AssetHeaderState {}
|
||||
class AssetHeaderInitial extends AssetHeaderState {}
|
||||
class AssetHeaderLoading extends AssetHeaderState {
|
||||
final AssetModel? previousData;
|
||||
AssetHeaderLoading({this.previousData});
|
||||
}
|
||||
class AssetHeaderLoaded extends AssetHeaderState {
|
||||
final AssetModel? data;
|
||||
AssetHeaderLoaded(this.data);
|
||||
}
|
||||
class AssetHeaderError extends AssetHeaderState {
|
||||
final String message;
|
||||
AssetHeaderError(this.message);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'asset_technical_event.dart';
|
||||
import 'asset_technical_state.dart';
|
||||
import '../../repositories/asset_repository.dart';
|
||||
|
||||
class AssetTechnicalBloc extends Bloc<AssetTechnicalEvent, AssetTechnicalState> {
|
||||
final AssetRepository repository;
|
||||
AssetTechnicalBloc({required this.repository}) : super(AssetTechnicalInitial()) {
|
||||
on<LoadAssetTechnical>((event, emit) async {
|
||||
emit(AssetTechnicalLoading());
|
||||
try {
|
||||
final data = await repository.getAssetTechnical(event.isin, event.forceRefresh, ticker: event.ticker);
|
||||
emit(AssetTechnicalLoaded(data));
|
||||
} catch (e) {
|
||||
emit(AssetTechnicalError(e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
abstract class AssetTechnicalEvent {}
|
||||
class LoadAssetTechnical extends AssetTechnicalEvent {
|
||||
final String isin;
|
||||
final bool forceRefresh;
|
||||
final String? ticker;
|
||||
LoadAssetTechnical(this.isin, {this.forceRefresh = false, this.ticker});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import '../../models/technical_analysis_model.dart';
|
||||
|
||||
abstract class AssetTechnicalState {}
|
||||
class AssetTechnicalInitial extends AssetTechnicalState {}
|
||||
class AssetTechnicalLoading extends AssetTechnicalState {}
|
||||
class AssetTechnicalLoaded extends AssetTechnicalState {
|
||||
final TechnicalAnalysisModel? data;
|
||||
AssetTechnicalLoaded(this.data);
|
||||
}
|
||||
class AssetTechnicalError extends AssetTechnicalState {
|
||||
final String message;
|
||||
AssetTechnicalError(this.message);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'asset_trades_event.dart';
|
||||
import 'asset_trades_state.dart';
|
||||
import '../../repositories/asset_repository.dart';
|
||||
|
||||
class AssetTradesBloc extends Bloc<AssetTradesEvent, AssetTradesState> {
|
||||
final AssetRepository repository;
|
||||
|
||||
AssetTradesBloc({required this.repository}) : super(AssetTradesInitial()) {
|
||||
on<LoadAssetTrades>((event, emit) async {
|
||||
emit(AssetTradesLoading());
|
||||
try {
|
||||
final data = await repository.getAssetTrades(event.isin, event.status);
|
||||
emit(AssetTradesLoaded(data));
|
||||
} catch (e) {
|
||||
emit(AssetTradesError(e.toString()));
|
||||
}
|
||||
});
|
||||
on<TriggerManualAnalysis>((event, emit) async {
|
||||
try {
|
||||
await repository.triggerManualAnalysis(event.isin, payload: event.payload);
|
||||
add(LoadAssetTrades(event.isin));
|
||||
} catch (e) {
|
||||
emit(AssetTradesError("Failed to trigger manual analysis: $e"));
|
||||
}
|
||||
});
|
||||
on<RejectTradeEvent>((event, emit) async {
|
||||
try {
|
||||
await repository.rejectTrade(event.tradeId);
|
||||
add(LoadAssetTrades(event.isin));
|
||||
} catch (e) {
|
||||
emit(AssetTradesError("Failed to reject trade: $e"));
|
||||
}
|
||||
});
|
||||
on<AcceptTradeEvent>((event, emit) async {
|
||||
try {
|
||||
await repository.acceptTrade(event.tradeAcceptanceDto);
|
||||
add(LoadAssetTrades(event.isin));
|
||||
} catch (e) {
|
||||
emit(AssetTradesError("Failed to accept trade: $e"));
|
||||
}
|
||||
});
|
||||
on<CloseTradeEvent>((event, emit) async {
|
||||
try {
|
||||
await repository.closeTrade(event.tradeId, event.exitPrice);
|
||||
add(LoadAssetTrades(event.isin));
|
||||
} catch (e) {
|
||||
emit(AssetTradesError("Failed to close trade: $e"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:finlytic_app/features/trades/models/trade_acceptance_dto.dart';
|
||||
|
||||
import 'package:finlytic_app/features/asset_detail/models/manual_analysis_request_dto.dart';
|
||||
|
||||
abstract class AssetTradesEvent {}
|
||||
class LoadAssetTrades extends AssetTradesEvent {
|
||||
final String isin;
|
||||
final String? status;
|
||||
LoadAssetTrades(this.isin, {this.status});
|
||||
}
|
||||
class TriggerManualAnalysis extends AssetTradesEvent {
|
||||
final String isin;
|
||||
final ManualAnalysisRequestDto? payload;
|
||||
TriggerManualAnalysis(this.isin, {this.payload});
|
||||
}
|
||||
class RejectTradeEvent extends AssetTradesEvent {
|
||||
final String tradeId;
|
||||
final String isin;
|
||||
RejectTradeEvent(this.tradeId, this.isin);
|
||||
}
|
||||
class AcceptTradeEvent extends AssetTradesEvent {
|
||||
final TradeAcceptanceDto tradeAcceptanceDto;
|
||||
final String isin;
|
||||
AcceptTradeEvent(this.tradeAcceptanceDto, this.isin);
|
||||
}
|
||||
class CloseTradeEvent extends AssetTradesEvent {
|
||||
final String tradeId;
|
||||
final String isin;
|
||||
final double exitPrice;
|
||||
CloseTradeEvent(this.tradeId, this.isin, this.exitPrice);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import '../../../trades/models/trade_model.dart';
|
||||
|
||||
abstract class AssetTradesState {}
|
||||
class AssetTradesInitial extends AssetTradesState {}
|
||||
class AssetTradesLoading extends AssetTradesState {}
|
||||
class AssetTradesLoaded extends AssetTradesState {
|
||||
final List<TradeModel> data;
|
||||
AssetTradesLoaded(this.data);
|
||||
}
|
||||
class AssetTradesError extends AssetTradesState {
|
||||
final String message;
|
||||
AssetTradesError(this.message);
|
||||
}
|
||||
Reference in New Issue
Block a user