improved performance and battery usage on older devices
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter_app_badge/flutter_app_badge.dart';
|
||||
@@ -6,6 +7,7 @@ import 'package:flutter_app_badge/flutter_app_badge.dart';
|
||||
import '../../../../../api/marianumcloud/talk/actions/talk_actions.dart';
|
||||
import '../../../../../api/marianumcloud/talk/chat/get_chat_response.dart';
|
||||
import '../../../../../api/marianumcloud/talk/room/get_room_response.dart';
|
||||
import '../../../../../utils/session_single_flight.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_list_repository.dart';
|
||||
@@ -74,13 +76,32 @@ class ChatListBloc
|
||||
renew: renew,
|
||||
onError: (e) => capturedError = e,
|
||||
);
|
||||
_lastRoomsJson = null;
|
||||
add(DataGathered((s) => s.copyWith(rooms: rooms)));
|
||||
_updateAppBadge(rooms);
|
||||
|
||||
if (capturedError != null) throw capturedError!;
|
||||
}
|
||||
|
||||
Future<void> refresh({bool renew = true, bool silent = false}) async {
|
||||
final SessionSingleFlight _refreshFlight = SessionSingleFlight();
|
||||
|
||||
/// Concurrent callers (tab switch, poll, push, resume) join the running
|
||||
/// refresh instead of each fetching and re-emitting the full room list.
|
||||
Future<void> refresh({bool renew = true, bool silent = false}) =>
|
||||
_refreshFlight.run(() => _refresh(renew: renew, silent: silent));
|
||||
|
||||
/// Skips the refresh when the list was fetched within [maxAge].
|
||||
Future<void> refreshIfOlderThan(Duration maxAge) {
|
||||
final lastFetch = state.lastFetch;
|
||||
if (lastFetch != null &&
|
||||
DateTime.now().millisecondsSinceEpoch - lastFetch <
|
||||
maxAge.inMilliseconds) {
|
||||
return Future.value();
|
||||
}
|
||||
return refresh();
|
||||
}
|
||||
|
||||
Future<void> _refresh({required bool renew, required bool silent}) async {
|
||||
if (!silent) add(RefetchStarted<ChatListState>());
|
||||
Object? capturedError;
|
||||
try {
|
||||
@@ -88,6 +109,11 @@ class ChatListBloc
|
||||
renew: renew,
|
||||
onError: (e) => capturedError = e,
|
||||
);
|
||||
if (silent) {
|
||||
if (_isUnchanged(rooms)) return;
|
||||
} else {
|
||||
_lastRoomsJson = null;
|
||||
}
|
||||
add(DataGathered((s) => s.copyWith(rooms: rooms)));
|
||||
_updateAppBadge(rooms);
|
||||
} catch (e) {
|
||||
@@ -96,6 +122,23 @@ class ChatListBloc
|
||||
if (capturedError != null) addLoadingError(capturedError!);
|
||||
}
|
||||
|
||||
// Encoded room data of the last applied poll result; response headers are
|
||||
// left out because they differ on every request.
|
||||
String? _lastRoomsJson;
|
||||
|
||||
/// A background poll that returns the same rooms would otherwise rebuild the
|
||||
/// list and re-persist the whole state every 15 s.
|
||||
bool _isUnchanged(GetRoomResponse rooms) {
|
||||
final encoded = jsonEncode(rooms.data);
|
||||
final unchanged =
|
||||
encoded == _lastRoomsJson &&
|
||||
innerState?.rooms != null &&
|
||||
state.error == null &&
|
||||
!state.isLoading;
|
||||
_lastRoomsJson = encoded;
|
||||
return unchanged;
|
||||
}
|
||||
|
||||
/// Creates (or resolves) a 1:1 chat and returns its room token, or null in
|
||||
/// demo mode. Refreshes the list so the room shows up.
|
||||
Future<String?> createDirectChat(String invite) async {
|
||||
@@ -171,6 +214,7 @@ class ChatListBloc
|
||||
}).toSet();
|
||||
if (!changed) return;
|
||||
final newRooms = GetRoomResponse(updated)..headers = rooms.headers;
|
||||
_lastRoomsJson = null;
|
||||
add(Emit((s) => s.copyWith(rooms: newRooms)));
|
||||
_updateAppBadge(newRooms);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user