mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: invalid drag start index
This commit is contained in:
parent
c25237c767
commit
a0d7f114c0
@ -17,21 +17,18 @@ class FlexDragTargetData extends DragTargetData {
|
||||
|
||||
Size? get feedbackSize => state.feedbackSize;
|
||||
|
||||
/// Indicate the dragTarget come from which [ReorderFlex].
|
||||
final DraggingReorderFlex draggingReorderFlex;
|
||||
|
||||
final String dragTargetId;
|
||||
|
||||
final ReoderFlexItem reorderFlexItem;
|
||||
final String reorderFlexId;
|
||||
|
||||
String get reorderFlexId => draggingReorderFlex.reorderFlexId;
|
||||
final ReoderFlexItem reorderFlexItem;
|
||||
|
||||
FlexDragTargetData({
|
||||
required this.dragTargetId,
|
||||
required this.draggingIndex,
|
||||
required this.reorderFlexId,
|
||||
required this.reorderFlexItem,
|
||||
required this.state,
|
||||
required this.draggingReorderFlex,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -40,11 +37,6 @@ class FlexDragTargetData extends DragTargetData {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DraggingReorderFlex {
|
||||
String get reorderFlexId;
|
||||
ReoderFlexItem itemAtIndex(int index);
|
||||
}
|
||||
|
||||
class DraggingState {
|
||||
final String id;
|
||||
|
||||
@ -85,10 +77,12 @@ class DraggingState {
|
||||
if (_draggingFeedbackSize == null) {
|
||||
return Size.zero;
|
||||
}
|
||||
return _draggingFeedbackSize! + const Offset(_dropAreaMargin, _dropAreaMargin);
|
||||
return _draggingFeedbackSize! +
|
||||
const Offset(_dropAreaMargin, _dropAreaMargin);
|
||||
}
|
||||
|
||||
void startDragging(Widget draggingWidget, int draggingWidgetIndex, Size? draggingWidgetSize) {
|
||||
void startDragging(Widget draggingWidget, int draggingWidgetIndex,
|
||||
Size? draggingWidgetSize) {
|
||||
///
|
||||
assert(draggingWidgetIndex >= 0);
|
||||
|
||||
|
@ -25,7 +25,8 @@ abstract class ReorderFlexDragTargetInterceptor {
|
||||
|
||||
abstract class OverlapReorderFlexDragTargetDelegate {}
|
||||
|
||||
class OverlapReorderFlexDragTargetInteceptor extends ReorderFlexDragTargetInterceptor {
|
||||
class OverlapReorderFlexDragTargetInteceptor
|
||||
extends ReorderFlexDragTargetInterceptor {
|
||||
final String reorderFlexId;
|
||||
final List<String> acceptedReorderFlexId;
|
||||
final OverlapReorderFlexDragTargetDelegate delegate;
|
||||
@ -49,7 +50,7 @@ class OverlapReorderFlexDragTargetInteceptor extends ReorderFlexDragTargetInterc
|
||||
required String dragTargetId,
|
||||
required int dragTargetIndex}) {
|
||||
if (dragTargetId == dragTargetData.reorderFlexId) {
|
||||
Log.debug('remove all phantom');
|
||||
// Log.debug('remove all phantom');
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -60,7 +61,7 @@ abstract class CrossReorderFlexDragTargetDelegate {
|
||||
bool acceptNewDragTargetData(
|
||||
String reorderFlexId,
|
||||
FlexDragTargetData dragTargetData,
|
||||
int index,
|
||||
int dragTargetIndex,
|
||||
);
|
||||
|
||||
void updateDragTargetData(
|
||||
@ -70,7 +71,8 @@ abstract class CrossReorderFlexDragTargetDelegate {
|
||||
);
|
||||
}
|
||||
|
||||
class CrossReorderFlexDragTargetInterceptor extends ReorderFlexDragTargetInterceptor {
|
||||
class CrossReorderFlexDragTargetInterceptor
|
||||
extends ReorderFlexDragTargetInterceptor {
|
||||
final String reorderFlexId;
|
||||
final List<String> acceptedReorderFlexIds;
|
||||
final CrossReorderFlexDragTargetDelegate delegate;
|
||||
@ -102,21 +104,24 @@ class CrossReorderFlexDragTargetInterceptor extends ReorderFlexDragTargetInterce
|
||||
|
||||
@override
|
||||
void onAccept(FlexDragTargetData dragTargetData) {
|
||||
Log.trace('[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on onAccept');
|
||||
Log.trace(
|
||||
'[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on onAccept');
|
||||
}
|
||||
|
||||
@override
|
||||
void onLeave(FlexDragTargetData dragTargetData) {
|
||||
Log.trace('[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on leave');
|
||||
Log.trace(
|
||||
'[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on leave');
|
||||
}
|
||||
|
||||
@override
|
||||
bool onWillAccept(
|
||||
{required BuildContext context,
|
||||
required ReorderFlexState reorderFlexState,
|
||||
required FlexDragTargetData dragTargetData,
|
||||
required String dragTargetId,
|
||||
required int dragTargetIndex}) {
|
||||
bool onWillAccept({
|
||||
required BuildContext context,
|
||||
required ReorderFlexState reorderFlexState,
|
||||
required FlexDragTargetData dragTargetData,
|
||||
required String dragTargetId,
|
||||
required int dragTargetIndex,
|
||||
}) {
|
||||
final isNewDragTarget = delegate.acceptNewDragTargetData(
|
||||
reorderFlexId,
|
||||
dragTargetData,
|
||||
@ -132,7 +137,6 @@ class CrossReorderFlexDragTargetInterceptor extends ReorderFlexDragTargetInterce
|
||||
|
||||
reorderFlexState.handleOnWillAccept(
|
||||
context,
|
||||
dragTargetData.draggingIndex,
|
||||
dragTargetIndex,
|
||||
);
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ typedef OnDragEnded = void Function();
|
||||
typedef OnReorder = void Function(int fromIndex, int toIndex);
|
||||
typedef OnDeleted = void Function(int deletedIndex);
|
||||
typedef OnInserted = void Function(int insertedIndex);
|
||||
typedef OnReveivePassedInPhantom = void Function(FlexDragTargetData dragTargetData, int phantomIndex);
|
||||
typedef OnReveivePassedInPhantom = void Function(
|
||||
FlexDragTargetData dragTargetData, int phantomIndex);
|
||||
|
||||
abstract class ReoderFlextDataSource {
|
||||
String get identifier;
|
||||
@ -32,7 +33,7 @@ class ReorderFlexConfig {
|
||||
const ReorderFlexConfig();
|
||||
}
|
||||
|
||||
class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
|
||||
class ReorderFlex extends StatefulWidget {
|
||||
final ReorderFlexConfig config;
|
||||
|
||||
final List<Widget> children;
|
||||
@ -68,16 +69,11 @@ class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
|
||||
@override
|
||||
State<ReorderFlex> createState() => ReorderFlexState();
|
||||
|
||||
@override
|
||||
String get reorderFlexId => dataSource.identifier;
|
||||
|
||||
@override
|
||||
ReoderFlexItem itemAtIndex(int index) {
|
||||
return dataSource.items[index];
|
||||
}
|
||||
}
|
||||
|
||||
class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerProviderStateMixin<ReorderFlex> {
|
||||
class ReorderFlexState extends State<ReorderFlex>
|
||||
with ReorderFlexMinxi, TickerProviderStateMixin<ReorderFlex> {
|
||||
/// Controls scrolls and measures scroll progress.
|
||||
late ScrollController _scrollController;
|
||||
ScrollPosition? _attachedScrollPosition;
|
||||
@ -113,7 +109,9 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
_attachedScrollPosition = null;
|
||||
}
|
||||
|
||||
_scrollController = widget.scrollController ?? PrimaryScrollController.of(context) ?? ScrollController();
|
||||
_scrollController = widget.scrollController ??
|
||||
PrimaryScrollController.of(context) ??
|
||||
ScrollController();
|
||||
|
||||
if (_scrollController.hasClients) {
|
||||
_attachedScrollPosition = Scrollable.of(context)?.position;
|
||||
@ -235,7 +233,9 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
]);
|
||||
} else if (childIndex == dragPhantomIndex) {
|
||||
return _buildDraggingContainer(
|
||||
children: shiftedIndex <= childIndex ? [dragTarget, disappearSpace] : [disappearSpace, dragTarget]);
|
||||
children: shiftedIndex <= childIndex
|
||||
? [dragTarget, disappearSpace]
|
||||
: [disappearSpace, dragTarget]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +256,9 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
]);
|
||||
} else if (childIndex == dragPhantomIndex) {
|
||||
return _buildDraggingContainer(
|
||||
children: shiftedIndex >= childIndex ? [disappearSpace, dragTarget] : [dragTarget, disappearSpace]);
|
||||
children: shiftedIndex >= childIndex
|
||||
? [disappearSpace, dragTarget]
|
||||
: [dragTarget, disappearSpace]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,22 +284,25 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
Widget child,
|
||||
int dragTargetIndex,
|
||||
) {
|
||||
final ReoderFlexItem reorderFlexItem = widget.dataSource.items[dragTargetIndex];
|
||||
final ReoderFlexItem reorderFlexItem =
|
||||
widget.dataSource.items[dragTargetIndex];
|
||||
return ReorderDragTarget<FlexDragTargetData>(
|
||||
dragTargetData: FlexDragTargetData(
|
||||
draggingIndex: dragTargetIndex,
|
||||
reorderFlexId: widget.reorderFlexId,
|
||||
reorderFlexItem: reorderFlexItem,
|
||||
state: dragState,
|
||||
draggingReorderFlex: widget,
|
||||
dragTargetId: reorderFlexItem.id,
|
||||
),
|
||||
onDragStarted: (draggingWidget, draggingIndex, size) {
|
||||
Log.debug("[DragTarget] Column${widget.dataSource.identifier} start dragging");
|
||||
Log.debug(
|
||||
"[DragTarget] Column${widget.dataSource.identifier} start dragging");
|
||||
_startDragging(draggingWidget, draggingIndex, size);
|
||||
widget.onDragStarted?.call(draggingIndex);
|
||||
},
|
||||
onDragEnded: (dragTargetData) {
|
||||
Log.debug("[DragTarget]: Column${widget.dataSource.identifier} end dragging");
|
||||
Log.debug(
|
||||
"[DragTarget]: Column${widget.dataSource.identifier} end dragging");
|
||||
|
||||
setState(() {
|
||||
_onReordered(
|
||||
@ -323,8 +328,7 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
)) {
|
||||
return true;
|
||||
} else {
|
||||
final dragIndex = dragTargetData.draggingIndex;
|
||||
return handleOnWillAccept(builderContext, dragIndex, dragTargetIndex);
|
||||
return handleOnWillAccept(builderContext, dragTargetIndex);
|
||||
}
|
||||
},
|
||||
onAccept: (dragTargetData) {
|
||||
@ -386,14 +390,17 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
});
|
||||
}
|
||||
|
||||
bool handleOnWillAccept(BuildContext context, int? dragIndex, int dragTargetIndex) {
|
||||
bool handleOnWillAccept(BuildContext context, int dragTargetIndex) {
|
||||
final dragIndex = dragState.dragStartIndex;
|
||||
|
||||
/// The [willAccept] will be true if the dargTarget is the widget that gets
|
||||
/// dragged and it is dragged on top of the other dragTargets.
|
||||
///
|
||||
Log.debug(
|
||||
'[$ReorderDragTarget] ${widget.dataSource.identifier} on will accept, dragIndex:$dragIndex, dragTargetIndex:$dragTargetIndex, count: ${widget.dataSource.items.length}');
|
||||
|
||||
bool willAccept = dragState.dragStartIndex == dragIndex && dragIndex != dragTargetIndex;
|
||||
bool willAccept =
|
||||
dragState.dragStartIndex == dragIndex && dragIndex != dragTargetIndex;
|
||||
setState(() {
|
||||
if (willAccept) {
|
||||
int shiftedIndex = dragState.calculateShiftedIndex(dragTargetIndex);
|
||||
@ -420,7 +427,8 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
}
|
||||
|
||||
Widget _wrapScrollView({required Widget child}) {
|
||||
if (widget.scrollController != null && PrimaryScrollController.of(context) == null) {
|
||||
if (widget.scrollController != null &&
|
||||
PrimaryScrollController.of(context) == null) {
|
||||
return child;
|
||||
} else {
|
||||
return SingleChildScrollView(
|
||||
@ -474,12 +482,14 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
void _scrollTo(BuildContext context) {
|
||||
if (_scrolling) return;
|
||||
final RenderObject contextObject = context.findRenderObject()!;
|
||||
final RenderAbstractViewport viewport = RenderAbstractViewport.of(contextObject)!;
|
||||
final RenderAbstractViewport viewport =
|
||||
RenderAbstractViewport.of(contextObject)!;
|
||||
// If and only if the current scroll offset falls in-between the offsets
|
||||
// necessary to reveal the selected context at the top or bottom of the
|
||||
// screen, then it is already on-screen.
|
||||
final double margin =
|
||||
widget.direction == Axis.horizontal ? dragState.dropAreaSize.width : dragState.dropAreaSize.height;
|
||||
final double margin = widget.direction == Axis.horizontal
|
||||
? dragState.dropAreaSize.width
|
||||
: dragState.dropAreaSize.height;
|
||||
if (_scrollController.hasClients) {
|
||||
final double scrollOffset = _scrollController.offset;
|
||||
final double topOffset = max(
|
||||
@ -490,7 +500,8 @@ class ReorderFlexState extends State<ReorderFlex> with ReorderFlexMinxi, TickerP
|
||||
_scrollController.position.maxScrollExtent,
|
||||
viewport.getOffsetToReveal(contextObject, 1.0).offset + margin,
|
||||
);
|
||||
final bool onScreen = scrollOffset <= topOffset && scrollOffset >= bottomOffset;
|
||||
final bool onScreen =
|
||||
scrollOffset <= topOffset && scrollOffset >= bottomOffset;
|
||||
|
||||
// If the context is off screen, then we request a scroll to make it visible.
|
||||
if (!onScreen) {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import '../../../flowy_board.dart';
|
||||
import '../../utils/log.dart';
|
||||
import '../flex/drag_state.dart';
|
||||
@ -15,7 +14,8 @@ mixin ColumnDataPhantomMixim {
|
||||
BoardColumnDataController? get;
|
||||
}
|
||||
|
||||
class BoardPhantomController extends OverlapReorderFlexDragTargetDelegate with CrossReorderFlexDragTargetDelegate {
|
||||
class BoardPhantomController extends OverlapReorderFlexDragTargetDelegate
|
||||
with CrossReorderFlexDragTargetDelegate {
|
||||
final BoardPhantomControllerDelegate delegate;
|
||||
|
||||
PhantomRecord? phantomRecord;
|
||||
@ -64,10 +64,16 @@ class BoardPhantomController extends OverlapReorderFlexDragTargetDelegate with C
|
||||
if (columnsState.isDragging(phantomRecord!.fromColumnId) == false) {
|
||||
return;
|
||||
}
|
||||
final item = delegate.controller(phantomRecord!.fromColumnId)?.removeAt(phantomRecord!.fromColumnIndex);
|
||||
final item = delegate
|
||||
.controller(phantomRecord!.fromColumnId)
|
||||
?.removeAt(phantomRecord!.fromColumnIndex);
|
||||
assert(item != null);
|
||||
assert(delegate.controller(phantomRecord!.toColumnId)?.items[phantomRecord!.toColumnIndex] is PhantomColumnItem);
|
||||
delegate.controller(phantomRecord!.toColumnId)?.replace(phantomRecord!.toColumnIndex, item!);
|
||||
assert(delegate
|
||||
.controller(phantomRecord!.toColumnId)
|
||||
?.items[phantomRecord!.toColumnIndex] is PhantomColumnItem);
|
||||
delegate
|
||||
.controller(phantomRecord!.toColumnId)
|
||||
?.replace(phantomRecord!.toColumnIndex, item!);
|
||||
|
||||
Log.debug("[$BoardPhantomController] did move ${phantomRecord.toString()}");
|
||||
phantomRecord = null;
|
||||
@ -76,24 +82,28 @@ class BoardPhantomController extends OverlapReorderFlexDragTargetDelegate with C
|
||||
void _updatePhantom(
|
||||
String toColumnId,
|
||||
FlexDragTargetData dragTargetData,
|
||||
int phantomIndex,
|
||||
int dragTargetIndex,
|
||||
) {
|
||||
final columnDataController = delegate.controller(toColumnId);
|
||||
final index = columnDataController?.items.indexWhere((item) => item.isPhantom);
|
||||
final index =
|
||||
columnDataController?.items.indexWhere((item) => item.isPhantom);
|
||||
if (index == null) return;
|
||||
|
||||
assert(index != -1);
|
||||
if (index != -1) {
|
||||
if (index != phantomIndex) {
|
||||
if (index != dragTargetIndex) {
|
||||
// Log.debug('[$BoardPhantomController] update $toColumnId:$index to $toColumnId:$phantomIndex');
|
||||
final item = columnDataController!.removeAt(index, notify: false);
|
||||
columnDataController.insert(phantomIndex, item, notify: false);
|
||||
columnDataController.insert(dragTargetIndex, item, notify: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _removePhantom(String columnId) {
|
||||
final index = delegate.controller(columnId)?.items.indexWhere((item) => item.isPhantom);
|
||||
final index = delegate
|
||||
.controller(columnId)
|
||||
?.items
|
||||
.indexWhere((item) => item.isPhantom);
|
||||
|
||||
if (index == null) return;
|
||||
|
||||
@ -124,7 +134,9 @@ class BoardPhantomController extends OverlapReorderFlexDragTargetDelegate with C
|
||||
);
|
||||
columnsState.addColumnListener(toColumnId, phantomContext);
|
||||
Log.debug('$phantomContext');
|
||||
delegate.controller(toColumnId)?.insert(phantomIndex, PhantomColumnItem(phantomContext));
|
||||
delegate
|
||||
.controller(toColumnId)
|
||||
?.insert(phantomIndex, PhantomColumnItem(phantomContext));
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
@ -152,10 +164,14 @@ class BoardPhantomController extends OverlapReorderFlexDragTargetDelegate with C
|
||||
}
|
||||
|
||||
@override
|
||||
bool acceptNewDragTargetData(String reorderFlexId, FlexDragTargetData dragTargetData, int index) {
|
||||
bool acceptNewDragTargetData(
|
||||
String reorderFlexId,
|
||||
FlexDragTargetData dragTargetData,
|
||||
int dragTargetIndex,
|
||||
) {
|
||||
if (phantomRecord == null) {
|
||||
_updatePhantomRecord(reorderFlexId, dragTargetData, index);
|
||||
_insertPhantom(reorderFlexId, dragTargetData, index);
|
||||
_updatePhantomRecord(reorderFlexId, dragTargetData, dragTargetIndex);
|
||||
_insertPhantom(reorderFlexId, dragTargetData, dragTargetIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -165,21 +181,26 @@ class BoardPhantomController extends OverlapReorderFlexDragTargetDelegate with C
|
||||
_removePhantom(phantomRecord!.toColumnId);
|
||||
|
||||
/// Update the record and insert the phantom to new column.
|
||||
_updatePhantomRecord(reorderFlexId, dragTargetData, index);
|
||||
_insertPhantom(reorderFlexId, dragTargetData, index);
|
||||
_updatePhantomRecord(reorderFlexId, dragTargetData, dragTargetIndex);
|
||||
_insertPhantom(reorderFlexId, dragTargetData, dragTargetIndex);
|
||||
}
|
||||
|
||||
return isNewDragTarget;
|
||||
}
|
||||
|
||||
@override
|
||||
void updateDragTargetData(String reorderFlexId, FlexDragTargetData dragTargetData, int dragTargetIndex) {
|
||||
void updateDragTargetData(
|
||||
String reorderFlexId,
|
||||
FlexDragTargetData dragTargetData,
|
||||
int dragTargetIndex,
|
||||
) {
|
||||
phantomRecord?.updateInsertedIndex(dragTargetIndex);
|
||||
|
||||
assert(phantomRecord != null);
|
||||
if (phantomRecord!.toColumnId == reorderFlexId) {
|
||||
/// Update the existing phantom index
|
||||
_updatePhantom(phantomRecord!.toColumnId, dragTargetData, dragTargetIndex);
|
||||
_updatePhantom(
|
||||
phantomRecord!.toColumnId, dragTargetData, dragTargetIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,7 +225,8 @@ class PhantomRecord {
|
||||
if (fromColumnIndex == index) {
|
||||
return;
|
||||
}
|
||||
Log.debug('[$PhantomRecord] Update Column$fromColumnId remove position to $index');
|
||||
Log.debug(
|
||||
'[$PhantomRecord] Update Column$fromColumnId remove position to $index');
|
||||
fromColumnIndex = index;
|
||||
}
|
||||
|
||||
@ -213,7 +235,8 @@ class PhantomRecord {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.debug('[$PhantomRecord] Column$toColumnId update position $toColumnIndex -> $index');
|
||||
Log.debug(
|
||||
'[$PhantomRecord] Column$toColumnId update position $toColumnIndex -> $index');
|
||||
toColumnIndex = index;
|
||||
}
|
||||
|
||||
@ -226,7 +249,8 @@ class PhantomRecord {
|
||||
class PhantomColumnItem extends ColumnItem {
|
||||
final PassthroughPhantomContext phantomContext;
|
||||
|
||||
PhantomColumnItem(PassthroughPhantomContext insertedPhantom) : phantomContext = insertedPhantom;
|
||||
PhantomColumnItem(PassthroughPhantomContext insertedPhantom)
|
||||
: phantomContext = insertedPhantom;
|
||||
|
||||
@override
|
||||
bool get isPhantom => true;
|
||||
@ -236,8 +260,9 @@ class PhantomColumnItem extends ColumnItem {
|
||||
|
||||
Size? get feedbackSize => phantomContext.feedbackSize;
|
||||
|
||||
Widget get draggingWidget =>
|
||||
phantomContext.draggingWidget == null ? const SizedBox() : phantomContext.draggingWidget!;
|
||||
Widget get draggingWidget => phantomContext.draggingWidget == null
|
||||
? const SizedBox()
|
||||
: phantomContext.draggingWidget!;
|
||||
}
|
||||
|
||||
class PassthroughPhantomContext extends FakeDragTargetEventTrigger
|
||||
|
Loading…
Reference in New Issue
Block a user