Files
Client/lib/state/app/modules/chat/bloc/chat_bloc.dart
T

97 lines
2.8 KiB
Dart

import '../../../../../api/errors/error_mapper.dart';
import '../../../infrastructure/loadable_state/loading_error.dart';
import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart';
import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart';
import '../repository/chat_repository.dart';
import 'chat_event.dart';
import 'chat_state.dart';
class ChatBloc extends LoadableHydratedBloc<ChatEvent, ChatState, ChatRepository> {
DateTime _lastTokenSet = DateTime.fromMillisecondsSinceEpoch(0);
@override
ChatRepository repository() => ChatRepository();
@override
ChatState fromNothing() => const ChatState();
@override
ChatState fromStorage(Map<String, dynamic> json) => ChatState.fromJson(json);
@override
Map<String, dynamic>? toStorage(ChatState state) => state.toJson();
@override
Future<void> gatherData() async {
final token = innerState?.currentToken ?? '';
if (token.isEmpty) {
add(DataGathered((s) => s));
return;
}
await _loadChat(token);
}
void setToken(String token) {
if (token == (innerState?.currentToken ?? '')) {
refresh();
return;
}
add(Emit((s) => s.copyWith(currentToken: token, chatResponse: null)));
add(RefetchStarted<ChatState>());
_loadChat(token);
}
void setReferenceMessageId(int? messageId) {
add(Emit((s) => s.copyWith(referenceMessageId: messageId)));
}
void refresh() {
final token = innerState?.currentToken ?? '';
if (token.isEmpty) return;
add(RefetchStarted<ChatState>());
_loadChat(token);
}
Future<void> _loadChat(String token) async {
final requestStart = DateTime.now();
_lastTokenSet = requestStart;
bool stillCurrent() {
if (_lastTokenSet.isAfter(requestStart)) return false;
if ((innerState?.currentToken ?? '') != token) return false;
return true;
}
Object? capturedError;
try {
await repo.data.getChat(
token: token,
onCacheData: (data) {
if (!stillCurrent()) return;
// Cache hit: show data immediately but preserve lastFetch — the
// cached payload may be stale and we don't want the UI to claim a
// fresh fetch just happened.
add(Emit((s) => s.copyWith(chatResponse: data)));
},
onNetworkData: (data) {
if (!stillCurrent()) return;
add(DataGathered((s) => s.copyWith(chatResponse: data)));
},
onError: (e) => capturedError = e,
);
} catch (e) {
capturedError = e;
}
if (!stillCurrent()) return;
if (capturedError != null) {
add(Error(LoadingError(
message: errorToUserMessage(capturedError),
technicalDetails: errorToTechnicalDetails(capturedError),
allowRetry: errorAllowsRetry(capturedError),
)));
}
}
}