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({
|
Label copyWith({
|
||||||
Id? id,
|
Id? id,
|
||||||
KeyWordIdentifier? keyword,
|
KeyWordIdentifier? keyword,
|
||||||
String? displayName,
|
String? displayName,
|
||||||
HexColor? color,
|
HexColor? color,
|
||||||
String? description,
|
String? description,
|
||||||
|
bool? readOnly,
|
||||||
}) {
|
}) {
|
||||||
return Label(
|
return Label(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
@@ -42,6 +45,7 @@ extension LabelExtension on Label {
|
|||||||
keyword: keyword ?? this.keyword,
|
keyword: keyword ?? this.keyword,
|
||||||
color: color ?? this.color,
|
color: color ?? this.color,
|
||||||
description: description ?? this.description,
|
description: description ?? this.description,
|
||||||
|
readOnly: readOnly ?? this.readOnly,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export 'package:labels/model/label.dart';
|
export 'package:labels/model/label.dart';
|
||||||
export 'package:labels/model/hex_color.dart';
|
export 'package:labels/model/hex_color.dart';
|
||||||
|
export 'package:labels/model/labels_capability.dart';
|
||||||
|
|
||||||
export 'package:labels/utils/labels_constants.dart';
|
export 'package:labels/utils/labels_constants.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class Label with EquatableMixin {
|
|||||||
final String? displayName;
|
final String? displayName;
|
||||||
final HexColor? color;
|
final HexColor? color;
|
||||||
final String? description;
|
final String? description;
|
||||||
|
final bool? readOnly;
|
||||||
|
|
||||||
Label({
|
Label({
|
||||||
this.id,
|
this.id,
|
||||||
@@ -31,6 +32,7 @@ class Label with EquatableMixin {
|
|||||||
this.displayName,
|
this.displayName,
|
||||||
this.color,
|
this.color,
|
||||||
this.description,
|
this.description,
|
||||||
|
this.readOnly,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Label.fromJson(Map<String, dynamic> json) => _$LabelFromJson(json);
|
factory Label.fromJson(Map<String, dynamic> json) => _$LabelFromJson(json);
|
||||||
@@ -44,5 +46,6 @@ class Label with EquatableMixin {
|
|||||||
displayName,
|
displayName,
|
||||||
color,
|
color,
|
||||||
description,
|
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/vacation_capability.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/capability/web_socket_ticket_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: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/saas/saas_account_capability.dart';
|
||||||
import 'package:model/support/contact_support_capability.dart';
|
import 'package:model/support/contact_support_capability.dart';
|
||||||
import 'package:scribe/scribe/ai/presentation/model/ai_capability.dart';
|
import 'package:scribe/scribe/ai/presentation/model/ai_capability.dart';
|
||||||
@@ -41,6 +42,8 @@ class CapabilityPropertiesConverter {
|
|||||||
return properties.toJson();
|
return properties.toJson();
|
||||||
} else if (properties is AICapability) {
|
} else if (properties is AICapability) {
|
||||||
return properties.toJson();
|
return properties.toJson();
|
||||||
|
} else if (properties is LabelsCapability) {
|
||||||
|
return properties.toJson();
|
||||||
} else if (properties is DefaultCapability) {
|
} else if (properties is DefaultCapability) {
|
||||||
return properties.properties;
|
return properties.properties;
|
||||||
} else if (properties is EmptyCapability) {
|
} 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/capability/capability_properties.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.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/download_all/download_all_capability.dart';
|
||||||
import 'package:model/mailbox/mailbox_constants.dart';
|
import 'package:model/mailbox/mailbox_constants.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
@@ -36,6 +38,7 @@ extension SessionExtensions on Session {
|
|||||||
capabilityServerSettings: SettingsCapability.deserialize,
|
capabilityServerSettings: SettingsCapability.deserialize,
|
||||||
linagoraSaaSCapability: SaaSAccountCapability.deserialize,
|
linagoraSaaSCapability: SaaSAccountCapability.deserialize,
|
||||||
AiScribeConstants.aiCapability: AICapability.fromJson,
|
AiScribeConstants.aiCapability: AICapability.fromJson,
|
||||||
|
LabelsConstants.labelsCapability: LabelsCapability.fromJson,
|
||||||
};
|
};
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
@@ -199,4 +202,22 @@ extension SessionExtensions on Session {
|
|||||||
return null;
|
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/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/state.dart';
|
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||||
import 'package:labels/extensions/list_label_extension.dart';
|
import 'package:labels/labels.dart';
|
||||||
import 'package:labels/model/label.dart';
|
|
||||||
import 'package:labels/utils/labels_constants.dart';
|
|
||||||
import 'package:model/mailbox/expand_mode.dart';
|
import 'package:model/mailbox/expand_mode.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_controller.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/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/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/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/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/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/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_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/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_action_type_extension.dart';
|
||||||
@@ -53,6 +52,7 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
State? _currentLabelState;
|
State? _currentLabelState;
|
||||||
AccountId? _accountId;
|
AccountId? _accountId;
|
||||||
Session? _session;
|
Session? _session;
|
||||||
|
LabelsCapability? _labelsCapability;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@@ -64,9 +64,17 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
return LabelsConstants.labelsCapability.isSupported(session, accountId);
|
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) {
|
void checkLabelSettingState(Session session, AccountId accountId) {
|
||||||
_session = session;
|
_session = session;
|
||||||
_accountId = accountId;
|
_accountId = accountId;
|
||||||
|
_labelsCapability = _session?.getLabelsCapability(accountId);
|
||||||
_getLabelSettingStateInteractor =
|
_getLabelSettingStateInteractor =
|
||||||
getBinding<GetLabelSettingStateInteractor>();
|
getBinding<GetLabelSettingStateInteractor>();
|
||||||
if (_getLabelSettingStateInteractor != null) {
|
if (_getLabelSettingStateInteractor != null) {
|
||||||
@@ -245,6 +253,7 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
_currentLabelState = null;
|
_currentLabelState = null;
|
||||||
_accountId = null;
|
_accountId = null;
|
||||||
_session = null;
|
_session = null;
|
||||||
|
_labelsCapability = null;
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -413,6 +413,7 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
|||||||
if (isLabelAvailable) {
|
if (isLabelAvailable) {
|
||||||
final labelListExpandMode = labelController.labelListExpandMode.value;
|
final labelListExpandMode = labelController.labelListExpandMode.value;
|
||||||
final labels = labelController.labels;
|
final labels = labelController.labels;
|
||||||
|
final shouldAskReadOnly = labelController.shouldAskReadOnly;
|
||||||
|
|
||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 400),
|
duration: const Duration(milliseconds: 400),
|
||||||
@@ -421,6 +422,7 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
|||||||
labels: labels,
|
labels: labels,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
|
shouldAskReadOnly: shouldAskReadOnly,
|
||||||
labelIdSelected: labelIdSelected,
|
labelIdSelected: labelIdSelected,
|
||||||
isMobileResponsive: isMobileResponsive,
|
isMobileResponsive: isMobileResponsive,
|
||||||
onOpenLabelCallback: (label) => controller.openMailbox(
|
onOpenLabelCallback: (label) => controller.openMailbox(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class LabelListItem extends StatefulWidget {
|
|||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final bool isDesktop;
|
final bool isDesktop;
|
||||||
final bool isSelected;
|
final bool isSelected;
|
||||||
|
final bool shouldAskReadOnly;
|
||||||
final bool isMobileResponsive;
|
final bool isMobileResponsive;
|
||||||
final OnOpenLabelCallback onOpenLabelCallback;
|
final OnOpenLabelCallback onOpenLabelCallback;
|
||||||
final OnOpenLabelContextMenuAction? onOpenContextMenu;
|
final OnOpenLabelContextMenuAction? onOpenContextMenu;
|
||||||
@@ -28,6 +29,7 @@ class LabelListItem extends StatefulWidget {
|
|||||||
required this.onOpenLabelCallback,
|
required this.onOpenLabelCallback,
|
||||||
this.isDesktop = false,
|
this.isDesktop = false,
|
||||||
this.isSelected = false,
|
this.isSelected = false,
|
||||||
|
this.shouldAskReadOnly = false,
|
||||||
this.isMobileResponsive = false,
|
this.isMobileResponsive = false,
|
||||||
this.onOpenContextMenu,
|
this.onOpenContextMenu,
|
||||||
this.onLongPressLabelItemAction,
|
this.onLongPressLabelItemAction,
|
||||||
@@ -75,7 +77,26 @@ class _LabelListItemState extends State<LabelListItem> {
|
|||||||
: MailboxItemWidgetStyles.mobileLabelIconSpace;
|
: 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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -85,7 +106,7 @@ class _LabelListItemState extends State<LabelListItem> {
|
|||||||
borderRadius: _borderRadius,
|
borderRadius: _borderRadius,
|
||||||
onHover: _handleHoverChanged,
|
onHover: _handleHoverChanged,
|
||||||
onTap: () => widget.onOpenLabelCallback(widget.label),
|
onTap: () => widget.onOpenLabelCallback(widget.label),
|
||||||
onLongPress: PlatformInfo.isWebTouchDevice || PlatformInfo.isMobile
|
onLongPress: _isOnLongPressActive
|
||||||
? () => widget.onLongPressLabelItemAction?.call(widget.label)
|
? () => widget.onLongPressLabelItemAction?.call(widget.label)
|
||||||
: null,
|
: null,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class LabelListView extends StatelessWidget {
|
|||||||
final List<Label> labels;
|
final List<Label> labels;
|
||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final bool isDesktop;
|
final bool isDesktop;
|
||||||
|
final bool shouldAskReadOnly;
|
||||||
final Id? labelIdSelected;
|
final Id? labelIdSelected;
|
||||||
final OnOpenLabelCallback onOpenLabelCallback;
|
final OnOpenLabelCallback onOpenLabelCallback;
|
||||||
final bool isMobileResponsive;
|
final bool isMobileResponsive;
|
||||||
@@ -21,6 +22,7 @@ class LabelListView extends StatelessWidget {
|
|||||||
required this.imagePaths,
|
required this.imagePaths,
|
||||||
required this.onOpenLabelCallback,
|
required this.onOpenLabelCallback,
|
||||||
this.isDesktop = false,
|
this.isDesktop = false,
|
||||||
|
this.shouldAskReadOnly = false,
|
||||||
this.labelIdSelected,
|
this.labelIdSelected,
|
||||||
this.isMobileResponsive = false,
|
this.isMobileResponsive = false,
|
||||||
this.onOpenContextMenu,
|
this.onOpenContextMenu,
|
||||||
@@ -42,6 +44,7 @@ class LabelListView extends StatelessWidget {
|
|||||||
imagePaths: imagePaths,
|
imagePaths: imagePaths,
|
||||||
isSelected: label.id == labelIdSelected,
|
isSelected: label.id == labelIdSelected,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
|
shouldAskReadOnly: shouldAskReadOnly,
|
||||||
onOpenLabelCallback: onOpenLabelCallback,
|
onOpenLabelCallback: onOpenLabelCallback,
|
||||||
isMobileResponsive: isMobileResponsive,
|
isMobileResponsive: isMobileResponsive,
|
||||||
onOpenContextMenu: onOpenContextMenu,
|
onOpenContextMenu: onOpenContextMenu,
|
||||||
|
|||||||
Reference in New Issue
Block a user