simplified code across the app and consolidated duplicated helpers
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
@@ -10,6 +9,7 @@ import '../../../../../api/marianumcloud/talk/chat/long_poll_chat.dart';
|
||||
import '../../../../../api/marianumcloud/talk/room/get_room_response.dart';
|
||||
import '../../../../../api/marianumcloud/talk/set_read_marker/set_read_marker.dart';
|
||||
import '../../../../../api/marianumcloud/talk/set_read_marker/set_read_marker_params.dart';
|
||||
import '../../../../../utils/exponential_backoff.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 '../../chat_list/bloc/chat_list_bloc.dart';
|
||||
@@ -26,7 +26,6 @@ class ChatBloc
|
||||
// A loop parked in its request survives stop+start for the same token
|
||||
// (pause/resume); the generation retires it so only one loop polls.
|
||||
int _pollGeneration = 0;
|
||||
int _backoffMs = 0;
|
||||
int _lastKnownMessageId = 0;
|
||||
bool _appResumed = true;
|
||||
|
||||
@@ -68,8 +67,7 @@ class ChatBloc
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(ChatState state) {
|
||||
final response = state.chatResponse;
|
||||
if (response == null ||
|
||||
response.data.length <= _kMaxPersistedMessages) {
|
||||
if (response == null || response.data.length <= _kMaxPersistedMessages) {
|
||||
return state.toJson();
|
||||
}
|
||||
// Keep only the newest N; trimming drops older messages, so there is
|
||||
@@ -129,9 +127,6 @@ class ChatBloc
|
||||
add(Emit((s) => s.copyWith(referenceMessageId: messageId)));
|
||||
}
|
||||
|
||||
/// No-op when the bloc has already moved on to a different token: when
|
||||
/// popping a stacked chat (B over A), A's didPopNext runs setToken(A)
|
||||
/// before B's dispose fires.
|
||||
/// The chat view may still be popping when the sign-out resets this bloc,
|
||||
/// so leaveChat would find no token and the long-poll would keep running.
|
||||
@override
|
||||
@@ -141,6 +136,9 @@ class ChatBloc
|
||||
return super.reset();
|
||||
}
|
||||
|
||||
/// No-op when the bloc has already moved on to a different token: when
|
||||
/// popping a stacked chat (B over A), A's didPopNext runs setToken(A)
|
||||
/// before B's dispose fires.
|
||||
void leaveChat(String fromToken) {
|
||||
if ((innerState?.currentToken ?? '') != fromToken) return;
|
||||
_chatViewActive = false;
|
||||
@@ -272,7 +270,6 @@ class ChatBloc
|
||||
if (_pollingToken == token) return;
|
||||
_stopLongPoll();
|
||||
_pollingToken = token;
|
||||
_backoffMs = 0;
|
||||
_lastKnownMessageId = _maxMessageId(innerState?.chatResponse);
|
||||
unawaited(_pollLoop(token, ++_pollGeneration));
|
||||
}
|
||||
@@ -280,11 +277,11 @@ class ChatBloc
|
||||
void _stopLongPoll() {
|
||||
_pollingToken = null;
|
||||
_pollGeneration++;
|
||||
_backoffMs = 0;
|
||||
}
|
||||
|
||||
Future<void> _pollLoop(String token, int generation) async {
|
||||
bool active() => generation == _pollGeneration && !isClosed;
|
||||
var failures = 0;
|
||||
while (active()) {
|
||||
try {
|
||||
final response = await LongPollChat(
|
||||
@@ -293,7 +290,7 @@ class ChatBloc
|
||||
).run();
|
||||
|
||||
if (!active()) return;
|
||||
_backoffMs = 0;
|
||||
failures = 0;
|
||||
|
||||
if (response == null) continue;
|
||||
|
||||
@@ -318,8 +315,13 @@ class ChatBloc
|
||||
} on Object catch (e) {
|
||||
if (!active()) return;
|
||||
log('LongPoll error for $token: $e');
|
||||
_backoffMs = _backoffMs == 0 ? 2000 : math.min(_backoffMs * 2, 30000);
|
||||
await Future.delayed(Duration(milliseconds: _backoffMs));
|
||||
await Future<void>.delayed(
|
||||
exponentialBackoff(
|
||||
++failures,
|
||||
base: const Duration(seconds: 2),
|
||||
max: const Duration(seconds: 30),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,14 +341,7 @@ class ChatBloc
|
||||
);
|
||||
return;
|
||||
}
|
||||
final byId = <int, GetChatResponseObject>{};
|
||||
for (final m in current.data) {
|
||||
byId[m.id] = m;
|
||||
}
|
||||
for (final m in incoming.data) {
|
||||
byId[m.id] = m;
|
||||
}
|
||||
final merged = GetChatResponse(byId.values.toSet())
|
||||
final merged = _mergeById(current, incoming, incomingWins: true)
|
||||
..headers = incoming.headers;
|
||||
add(DataGathered((s) => s.copyWith(chatResponse: merged)));
|
||||
}
|
||||
@@ -357,14 +352,7 @@ class ChatBloc
|
||||
void _applyOlderResponse(GetChatResponse older) {
|
||||
final current = innerState?.chatResponse;
|
||||
if (current == null) return;
|
||||
final byId = <int, GetChatResponseObject>{};
|
||||
for (final m in current.data) {
|
||||
byId[m.id] = m;
|
||||
}
|
||||
for (final m in older.data) {
|
||||
byId.putIfAbsent(m.id, () => m);
|
||||
}
|
||||
final merged = GetChatResponse(byId.values.toSet())
|
||||
final merged = _mergeById(current, older, incomingWins: false)
|
||||
..headers = current.headers;
|
||||
add(
|
||||
DataGathered(
|
||||
@@ -377,6 +365,22 @@ class ChatBloc
|
||||
);
|
||||
}
|
||||
|
||||
GetChatResponse _mergeById(
|
||||
GetChatResponse current,
|
||||
GetChatResponse incoming, {
|
||||
required bool incomingWins,
|
||||
}) {
|
||||
final byId = {for (final m in current.data) m.id: m};
|
||||
for (final m in incoming.data) {
|
||||
if (incomingWins) {
|
||||
byId[m.id] = m;
|
||||
} else {
|
||||
byId.putIfAbsent(m.id, () => m);
|
||||
}
|
||||
}
|
||||
return GetChatResponse(byId.values.toSet());
|
||||
}
|
||||
|
||||
int _maxMessageId(GetChatResponse? response) {
|
||||
if (response == null) return 0;
|
||||
var max = 0;
|
||||
@@ -386,8 +390,7 @@ class ChatBloc
|
||||
return max;
|
||||
}
|
||||
|
||||
int _minMessageId(GetChatResponse? response) {
|
||||
if (response == null) return 0;
|
||||
int _minMessageId(GetChatResponse response) {
|
||||
var min = 0;
|
||||
for (final m in response.data) {
|
||||
if (m.id <= 0) continue; // skip dummies
|
||||
|
||||
Reference in New Issue
Block a user