feat: Auto sync label from web socket
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import 'package:core/presentation/extensions/either_view_state_extension.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
|
||||
import 'package:labels/extensions/list_label_extension.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/state/get_label_changes_state.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/label_controller.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/utils/label_utils.dart';
|
||||
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_message.dart';
|
||||
|
||||
extension HandleLabelWebsocketExtension on LabelController {
|
||||
void refreshLabelChanges({required jmap.State newState}) {
|
||||
if (accountId == null ||
|
||||
session == null ||
|
||||
currentLabelState == null ||
|
||||
currentLabelState == newState ||
|
||||
isLabelSettingEnabled.isFalse) {
|
||||
return;
|
||||
}
|
||||
|
||||
webSocketQueueHandler?.enqueue(WebSocketMessage(newState: newState));
|
||||
}
|
||||
|
||||
Future<void> handleWebSocketMessage(WebSocketMessage message) async {
|
||||
try {
|
||||
final refreshViewState = await getLabelChangesInteractor!
|
||||
.execute(
|
||||
session!,
|
||||
accountId!,
|
||||
currentLabelState!,
|
||||
)
|
||||
.last;
|
||||
|
||||
final refreshState =
|
||||
refreshViewState.foldSuccessWithResult<GetLabelChangesSuccess>();
|
||||
|
||||
if (refreshState is GetLabelChangesSuccess) {
|
||||
await _handleGetLabelChangesSuccess(refreshState);
|
||||
} else {
|
||||
onDataFailureViewState(refreshState);
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
logWarning(
|
||||
'HandleLabelWebsocketExtension::handleWebSocketMessage: Exception $e');
|
||||
onError(e, stackTrace);
|
||||
}
|
||||
|
||||
if (currentLabelState != null) {
|
||||
webSocketQueueHandler
|
||||
?.removeMessagesUpToCurrent(currentLabelState!.value);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleGetLabelChangesSuccess(
|
||||
GetLabelChangesSuccess success,
|
||||
) async {
|
||||
final result = success.changesResult;
|
||||
|
||||
setCurrentLabelState(result.newState);
|
||||
|
||||
LabelUtils.applyLabelChanges(
|
||||
currentLabels: labels,
|
||||
created: result.createdLabels,
|
||||
updated: result.updatedLabels,
|
||||
destroyedIds: result.destroyedLabelIds,
|
||||
);
|
||||
|
||||
labels.sortByAlphabetically();
|
||||
labels.refresh();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:dartz/dartz.dart' hide State;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||
import 'package:labels/extensions/list_label_extension.dart';
|
||||
import 'package:labels/model/label.dart';
|
||||
import 'package:labels/utils/labels_constants.dart';
|
||||
@@ -19,12 +20,15 @@ import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_i
|
||||
import 'package:tmail_ui_user/features/labels/domain/usecases/edit_label_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/usecases/delete_a_label_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/usecases/get_all_label_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/usecases/get_label_changes_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_action_type_extension.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_websocket_extension.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/label_interactor_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_context_menu_mixin.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/state/get_label_setting_state.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_label_setting_state_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_queue_handler.dart';
|
||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||
import 'package:tmail_ui_user/main/exceptions/logic_exception.dart';
|
||||
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
||||
@@ -40,12 +44,26 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
||||
GetLabelSettingStateInteractor? _getLabelSettingStateInteractor;
|
||||
EditLabelInteractor? _editLabelInteractor;
|
||||
DeleteALabelInteractor? _deleteALabelInteractor;
|
||||
GetLabelChangesInteractor? _getLabelChangesInteractor;
|
||||
|
||||
WebSocketQueueHandler? _webSocketQueueHandler;
|
||||
State? _currentLabelState;
|
||||
AccountId? _accountId;
|
||||
Session? _session;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
_initWebSocketQueueHandler();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
bool isLabelCapabilitySupported(Session session, AccountId accountId) {
|
||||
return LabelsConstants.labelsCapability.isSupported(session, accountId);
|
||||
}
|
||||
|
||||
void checkLabelSettingState(AccountId accountId) {
|
||||
void checkLabelSettingState(Session session, AccountId accountId) {
|
||||
_session = session;
|
||||
_accountId = accountId;
|
||||
_getLabelSettingStateInteractor =
|
||||
getBinding<GetLabelSettingStateInteractor>();
|
||||
if (_getLabelSettingStateInteractor != null) {
|
||||
@@ -66,12 +84,34 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
||||
_createNewLabelInteractor = getBinding<CreateNewLabelInteractor>();
|
||||
_editLabelInteractor = getBinding<EditLabelInteractor>();
|
||||
_deleteALabelInteractor = getBinding<DeleteALabelInteractor>();
|
||||
_getLabelChangesInteractor = getBinding<GetLabelChangesInteractor>();
|
||||
}
|
||||
|
||||
EditLabelInteractor? get editLabelInteractor => _editLabelInteractor;
|
||||
|
||||
DeleteALabelInteractor? get deleteALabelInteractor => _deleteALabelInteractor;
|
||||
|
||||
GetLabelChangesInteractor? get getLabelChangesInteractor =>
|
||||
_getLabelChangesInteractor;
|
||||
|
||||
WebSocketQueueHandler? get webSocketQueueHandler =>
|
||||
_webSocketQueueHandler;
|
||||
|
||||
AccountId? get accountId => _accountId;
|
||||
|
||||
Session? get session => _session;
|
||||
|
||||
State? get currentLabelState => _currentLabelState;
|
||||
|
||||
void setCurrentLabelState(State? newState) => _currentLabelState = newState;
|
||||
|
||||
void _initWebSocketQueueHandler() {
|
||||
_webSocketQueueHandler = WebSocketQueueHandler(
|
||||
processMessageCallback: handleWebSocketMessage,
|
||||
onErrorCallback: onError,
|
||||
);
|
||||
}
|
||||
|
||||
void getAllLabels(AccountId accountId) {
|
||||
if (_getAllLabelInteractor == null) return;
|
||||
|
||||
@@ -177,6 +217,11 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
||||
_editLabelInteractor = null;
|
||||
_deleteALabelInteractor = null;
|
||||
_getLabelSettingStateInteractor = null;
|
||||
_webSocketQueueHandler?.dispose();
|
||||
_webSocketQueueHandler = null;
|
||||
_currentLabelState = null;
|
||||
_accountId = null;
|
||||
_session = null;
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,4 +243,28 @@ class LabelUtils {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
static void applyLabelChanges({
|
||||
required List<Label> currentLabels,
|
||||
required List<Label> created,
|
||||
required List<Label> updated,
|
||||
required List<Id> destroyedIds,
|
||||
}) {
|
||||
final idsToRemove = {
|
||||
...destroyedIds,
|
||||
...updated.map((label) => label.id),
|
||||
};
|
||||
|
||||
if (idsToRemove.isNotEmpty) {
|
||||
currentLabels.removeWhere((label) => idsToRemove.contains(label.id));
|
||||
}
|
||||
|
||||
if (created.isNotEmpty) {
|
||||
currentLabels.addAll(created);
|
||||
}
|
||||
|
||||
if (updated.isNotEmpty) {
|
||||
currentLabels.addAll(updated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user