TF-4383 Handle read only labels
This commit is contained in:
@@ -29,12 +29,15 @@ extension LabelExtension on Label {
|
||||
}
|
||||
}
|
||||
|
||||
bool get isReadOnly => readOnly == true;
|
||||
|
||||
Label copyWith({
|
||||
Id? id,
|
||||
KeyWordIdentifier? keyword,
|
||||
String? displayName,
|
||||
HexColor? color,
|
||||
String? description,
|
||||
bool? readOnly,
|
||||
}) {
|
||||
return Label(
|
||||
id: id ?? this.id,
|
||||
@@ -42,6 +45,7 @@ extension LabelExtension on Label {
|
||||
keyword: keyword ?? this.keyword,
|
||||
color: color ?? this.color,
|
||||
description: description ?? this.description,
|
||||
readOnly: readOnly ?? this.readOnly,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export 'package:labels/model/label.dart';
|
||||
export 'package:labels/model/hex_color.dart';
|
||||
export 'package:labels/model/labels_capability.dart';
|
||||
|
||||
export 'package:labels/utils/labels_constants.dart';
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ class Label with EquatableMixin {
|
||||
final String? displayName;
|
||||
final HexColor? color;
|
||||
final String? description;
|
||||
final bool? readOnly;
|
||||
|
||||
Label({
|
||||
this.id,
|
||||
@@ -31,6 +32,7 @@ class Label with EquatableMixin {
|
||||
this.displayName,
|
||||
this.color,
|
||||
this.description,
|
||||
this.readOnly,
|
||||
});
|
||||
|
||||
factory Label.fromJson(Map<String, dynamic> json) => _$LabelFromJson(json);
|
||||
@@ -44,5 +46,6 @@ class Label with EquatableMixin {
|
||||
displayName,
|
||||
color,
|
||||
description,
|
||||
readOnly,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'labels_capability.g.dart';
|
||||
|
||||
@JsonSerializable(explicitToJson: true, includeIfNull: false)
|
||||
class LabelsCapability extends CapabilityProperties {
|
||||
final int? version;
|
||||
|
||||
LabelsCapability({this.version});
|
||||
|
||||
factory LabelsCapability.fromJson(Map<String, dynamic> json) =>
|
||||
_$LabelsCapabilityFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LabelsCapabilityToJson(this);
|
||||
|
||||
static LabelsCapability deserialize(Map<String, dynamic> json) {
|
||||
return LabelsCapability.fromJson(json);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [version];
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import 'package:jmap_dart_client/jmap/core/capability/submission_capability.dart
|
||||
import 'package:jmap_dart_client/jmap/core/capability/vacation_capability.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/capability/web_socket_ticket_capability.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/capability/websocket_capability.dart';
|
||||
import 'package:labels/model/labels_capability.dart';
|
||||
import 'package:model/saas/saas_account_capability.dart';
|
||||
import 'package:model/support/contact_support_capability.dart';
|
||||
import 'package:scribe/scribe/ai/presentation/model/ai_capability.dart';
|
||||
@@ -41,6 +42,8 @@ class CapabilityPropertiesConverter {
|
||||
return properties.toJson();
|
||||
} else if (properties is AICapability) {
|
||||
return properties.toJson();
|
||||
} else if (properties is LabelsCapability) {
|
||||
return properties.toJson();
|
||||
} else if (properties is DefaultCapability) {
|
||||
return properties.properties;
|
||||
} else if (properties is EmptyCapability) {
|
||||
|
||||
@@ -12,6 +12,8 @@ import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart
|
||||
import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||
import 'package:labels/model/labels_capability.dart';
|
||||
import 'package:labels/utils/labels_constants.dart';
|
||||
import 'package:model/download_all/download_all_capability.dart';
|
||||
import 'package:model/mailbox/mailbox_constants.dart';
|
||||
import 'package:model/model.dart';
|
||||
@@ -36,6 +38,7 @@ extension SessionExtensions on Session {
|
||||
capabilityServerSettings: SettingsCapability.deserialize,
|
||||
linagoraSaaSCapability: SaaSAccountCapability.deserialize,
|
||||
AiScribeConstants.aiCapability: AICapability.fromJson,
|
||||
LabelsConstants.labelsCapability: LabelsCapability.fromJson,
|
||||
};
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@@ -199,4 +202,22 @@ extension SessionExtensions on Session {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
LabelsCapability? getLabelsCapability(AccountId accountId) {
|
||||
try {
|
||||
if (!LabelsConstants.labelsCapability.isSupported(this, accountId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final labelsCapability = getCapabilityProperties<LabelsCapability>(
|
||||
accountId,
|
||||
LabelsConstants.labelsCapability,
|
||||
);
|
||||
log('SessionExtensions::getLabelsCapability:labelsCapability = $labelsCapability');
|
||||
return labelsCapability;
|
||||
} catch (e) {
|
||||
logWarning('SessionExtensions::getLabelsCapability():[Exception] $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,19 +7,18 @@ 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';
|
||||
import 'package:labels/labels.dart';
|
||||
import 'package:model/mailbox/expand_mode.dart';
|
||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/state/create_new_label_state.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/state/get_all_label_state.dart';
|
||||
import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart';
|
||||
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/edit_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';
|
||||
@@ -53,6 +52,7 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
||||
State? _currentLabelState;
|
||||
AccountId? _accountId;
|
||||
Session? _session;
|
||||
LabelsCapability? _labelsCapability;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@@ -64,9 +64,17 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
||||
return LabelsConstants.labelsCapability.isSupported(session, accountId);
|
||||
}
|
||||
|
||||
LabelsCapability? get labelsCapability => _labelsCapability;
|
||||
|
||||
bool get shouldAskReadOnly {
|
||||
final labelVersion = labelsCapability?.version;
|
||||
return labelVersion != null && labelVersion >= 1;
|
||||
}
|
||||
|
||||
void checkLabelSettingState(Session session, AccountId accountId) {
|
||||
_session = session;
|
||||
_accountId = accountId;
|
||||
_labelsCapability = _session?.getLabelsCapability(accountId);
|
||||
_getLabelSettingStateInteractor =
|
||||
getBinding<GetLabelSettingStateInteractor>();
|
||||
if (_getLabelSettingStateInteractor != null) {
|
||||
@@ -245,6 +253,7 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
||||
_currentLabelState = null;
|
||||
_accountId = null;
|
||||
_session = null;
|
||||
_labelsCapability = null;
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,6 +413,7 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
||||
if (isLabelAvailable) {
|
||||
final labelListExpandMode = labelController.labelListExpandMode.value;
|
||||
final labels = labelController.labels;
|
||||
final shouldAskReadOnly = labelController.shouldAskReadOnly;
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
@@ -421,6 +422,7 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
||||
labels: labels,
|
||||
imagePaths: controller.imagePaths,
|
||||
isDesktop: isDesktop,
|
||||
shouldAskReadOnly: shouldAskReadOnly,
|
||||
labelIdSelected: labelIdSelected,
|
||||
isMobileResponsive: isMobileResponsive,
|
||||
onOpenLabelCallback: (label) => controller.openMailbox(
|
||||
|
||||
@@ -16,6 +16,7 @@ class LabelListItem extends StatefulWidget {
|
||||
final ImagePaths imagePaths;
|
||||
final bool isDesktop;
|
||||
final bool isSelected;
|
||||
final bool shouldAskReadOnly;
|
||||
final bool isMobileResponsive;
|
||||
final OnOpenLabelCallback onOpenLabelCallback;
|
||||
final OnOpenLabelContextMenuAction? onOpenContextMenu;
|
||||
@@ -28,6 +29,7 @@ class LabelListItem extends StatefulWidget {
|
||||
required this.onOpenLabelCallback,
|
||||
this.isDesktop = false,
|
||||
this.isSelected = false,
|
||||
this.shouldAskReadOnly = false,
|
||||
this.isMobileResponsive = false,
|
||||
this.onOpenContextMenu,
|
||||
this.onLongPressLabelItemAction,
|
||||
@@ -75,7 +77,26 @@ class _LabelListItemState extends State<LabelListItem> {
|
||||
: MailboxItemWidgetStyles.mobileLabelIconSpace;
|
||||
}
|
||||
|
||||
bool get _isMenuButtonVisible => _isContextMenuVisible || _isItemHovered;
|
||||
bool get _isMenuButtonVisible {
|
||||
final isActive = _isContextMenuVisible || _isItemHovered;
|
||||
|
||||
if (!widget.shouldAskReadOnly) {
|
||||
return isActive;
|
||||
}
|
||||
|
||||
return isActive && !widget.label.isReadOnly;
|
||||
}
|
||||
|
||||
bool get _isOnLongPressActive {
|
||||
final isTouchPlatform =
|
||||
PlatformInfo.isWebTouchDevice || PlatformInfo.isMobile;
|
||||
|
||||
if (!widget.shouldAskReadOnly) {
|
||||
return isTouchPlatform;
|
||||
}
|
||||
|
||||
return isTouchPlatform && !widget.label.isReadOnly;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -85,7 +106,7 @@ class _LabelListItemState extends State<LabelListItem> {
|
||||
borderRadius: _borderRadius,
|
||||
onHover: _handleHoverChanged,
|
||||
onTap: () => widget.onOpenLabelCallback(widget.label),
|
||||
onLongPress: PlatformInfo.isWebTouchDevice || PlatformInfo.isMobile
|
||||
onLongPress: _isOnLongPressActive
|
||||
? () => widget.onLongPressLabelItemAction?.call(widget.label)
|
||||
: null,
|
||||
child: Container(
|
||||
|
||||
@@ -9,6 +9,7 @@ class LabelListView extends StatelessWidget {
|
||||
final List<Label> labels;
|
||||
final ImagePaths imagePaths;
|
||||
final bool isDesktop;
|
||||
final bool shouldAskReadOnly;
|
||||
final Id? labelIdSelected;
|
||||
final OnOpenLabelCallback onOpenLabelCallback;
|
||||
final bool isMobileResponsive;
|
||||
@@ -21,6 +22,7 @@ class LabelListView extends StatelessWidget {
|
||||
required this.imagePaths,
|
||||
required this.onOpenLabelCallback,
|
||||
this.isDesktop = false,
|
||||
this.shouldAskReadOnly = false,
|
||||
this.labelIdSelected,
|
||||
this.isMobileResponsive = false,
|
||||
this.onOpenContextMenu,
|
||||
@@ -42,6 +44,7 @@ class LabelListView extends StatelessWidget {
|
||||
imagePaths: imagePaths,
|
||||
isSelected: label.id == labelIdSelected,
|
||||
isDesktop: isDesktop,
|
||||
shouldAskReadOnly: shouldAskReadOnly,
|
||||
onOpenLabelCallback: onOpenLabelCallback,
|
||||
isMobileResponsive: isMobileResponsive,
|
||||
onOpenContextMenu: onOpenContextMenu,
|
||||
|
||||
Reference in New Issue
Block a user