mirror of
https://gitee.com/openharmony/arkui_ace_engine
synced 2024-11-23 15:10:30 +00:00
NDK C-Api guesture回调,Mouse适配
Signed-off-by: aryawang <wangcaihui@huawei.com> Change-Id: Iab26d5465f11321007940c48388bfec2e8896a54
This commit is contained in:
parent
81dfce8b91
commit
08785b2137
@ -15,6 +15,7 @@
|
||||
|
||||
#include "frameworks/core/interfaces/native/node/touch_event_convertor.h"
|
||||
|
||||
#include "adapter/ohos/entrance/ace_container.h"
|
||||
#include "adapter/ohos/entrance/mmi_event_convertor.h"
|
||||
|
||||
namespace OHOS::Ace::NG {
|
||||
@ -24,4 +25,8 @@ TouchEvent ConvertToTouchEvent(const std::shared_ptr<MMI::PointerEvent>& srcPoin
|
||||
return Platform::ConvertTouchEvent(srcPointerEvent);
|
||||
}
|
||||
|
||||
void ConvertToMouseEvent(MouseEvent& mouseEvent, const std::shared_ptr<MMI::PointerEvent>& srcPointerEvent)
|
||||
{
|
||||
Platform::ConvertMouseEvent(srcPointerEvent, mouseEvent, Container::Current()->IsScenceBoardWindow());
|
||||
}
|
||||
}
|
@ -20,4 +20,6 @@ TouchEvent ConvertToTouchEvent(const std::shared_ptr<MMI::PointerEvent>& srcPoin
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void ConvertToMouseEvent(MouseEvent& mouseEvent, const std::shared_ptr<MMI::PointerEvent>& srcPointerEvent) {}
|
||||
}
|
@ -205,6 +205,8 @@ struct ArkUIMouseEvent {
|
||||
ArkUI_Int64 timeStamp;
|
||||
ArkUITouchPoint actionTouchPoint;
|
||||
ArkUI_Int32 subKind;
|
||||
ArkUI_Int32 sourceType;
|
||||
ArkUI_Int32 interceptResult;
|
||||
};
|
||||
|
||||
struct ArkUIStringAndFloat {
|
||||
|
@ -238,6 +238,7 @@ void GetGestureEvent(ArkUIAPIEventGestureAsyncEvent& ret, GestureEvent& info)
|
||||
ret.scale = info.GetScale();
|
||||
ret.pinchCenterX = info.GetPinchCenter().GetX();
|
||||
ret.pinchCenterY = info.GetPinchCenter().GetY();
|
||||
ret.source = static_cast<int32_t>(info.GetSourceDevice());
|
||||
}
|
||||
|
||||
void GetBaseGestureEvent(ArkUIAPIEventGestureAsyncEvent* ret, ArkUITouchEvent& rawInputEvent,
|
||||
@ -280,55 +281,67 @@ void setCancelActionFunc(Gesture* gestureRef, void* extraParam)
|
||||
gestureRef->SetOnActionCancelId(onActionCancel);
|
||||
}
|
||||
|
||||
void ConvertIMMEventToMouseEvent(GestureEvent& info, ArkUIMouseEvent& mouseEvent)
|
||||
{
|
||||
CHECK_NULL_VOID(info.GetPointerEvent());
|
||||
MouseEvent tempMouseEvent;
|
||||
NG::ConvertToMouseEvent(tempMouseEvent, info.GetPointerEvent());
|
||||
auto fingureBegin = std::begin(info.GetFingerList());
|
||||
auto fingureEnd = std::end(info.GetFingerList());
|
||||
mouseEvent.action = static_cast<int32_t>(tempMouseEvent.action);
|
||||
mouseEvent.sourceType = static_cast<int32_t>(tempMouseEvent.sourceType);
|
||||
mouseEvent.timeStamp = tempMouseEvent.time.time_since_epoch().count();
|
||||
mouseEvent.actionTouchPoint.pressure = 0.0f;
|
||||
mouseEvent.actionTouchPoint.nodeX = fingureBegin == fingureEnd ? 0.0f : fingureBegin->localLocation_.GetX();
|
||||
mouseEvent.actionTouchPoint.nodeY = fingureBegin == fingureEnd ? 0.0f : fingureBegin->localLocation_.GetY();
|
||||
mouseEvent.actionTouchPoint.windowX = fingureBegin == fingureEnd ? 0.0f : fingureBegin->globalLocation_.GetX();
|
||||
mouseEvent.actionTouchPoint.windowY = fingureBegin == fingureEnd ? 0.0f : fingureBegin->globalLocation_.GetY();
|
||||
mouseEvent.actionTouchPoint.screenX = tempMouseEvent.screenX;
|
||||
mouseEvent.actionTouchPoint.screenY = tempMouseEvent.screenY;
|
||||
mouseEvent.actionTouchPoint.toolType = static_cast<int32_t>(tempMouseEvent.sourceTool);
|
||||
}
|
||||
|
||||
void SendGestureEvent(GestureEvent& info, int32_t eventKind, void* extraParam)
|
||||
{
|
||||
ArkUINodeEvent eventData;
|
||||
eventData.kind = GESTURE_ASYNC_EVENT;
|
||||
eventData.nodeId = 0;
|
||||
eventData.extraParam = reinterpret_cast<ArkUI_Int64>(extraParam);
|
||||
eventData.gestureAsyncEvent.subKind = eventKind;
|
||||
GetGestureEvent(eventData.gestureAsyncEvent, info);
|
||||
if (info.GetSourceDevice() == SourceType::MOUSE) {
|
||||
ArkUIMouseEvent rawInputEvent;
|
||||
ConvertIMMEventToMouseEvent(info, rawInputEvent);
|
||||
eventData.gestureAsyncEvent.rawPointerEvent = &rawInputEvent;
|
||||
SendArkUIAsyncEvent(&eventData);
|
||||
return;
|
||||
}
|
||||
ArkUITouchEvent rawInputEvent;
|
||||
std::array<ArkUITouchPoint, MAX_POINTS> points;
|
||||
ConvertIMMEventToTouchEvent(info, rawInputEvent, points);
|
||||
eventData.gestureAsyncEvent.rawPointerEvent = &rawInputEvent;
|
||||
SendArkUIAsyncEvent(&eventData);
|
||||
}
|
||||
|
||||
void registerGestureEvent(ArkUIGesture* gesture, ArkUI_Uint32 actionTypeMask, void* extraParam)
|
||||
{
|
||||
Gesture* gestureRef = reinterpret_cast<Gesture*>(gesture);
|
||||
if (actionTypeMask & ARKUI_GESTURE_EVENT_ACTION_ACCEPT) {
|
||||
auto onActionAccept = [extraParam](GestureEvent& info) {
|
||||
ArkUINodeEvent eventData;
|
||||
eventData.kind = GESTURE_ASYNC_EVENT;
|
||||
eventData.nodeId = 0;
|
||||
eventData.extraParam = reinterpret_cast<ArkUI_Int64>(extraParam);
|
||||
eventData.gestureAsyncEvent.subKind = ON_ACTION_START;
|
||||
GetGestureEvent(eventData.gestureAsyncEvent, info);
|
||||
ArkUITouchEvent rawInputEvent;
|
||||
std::array<ArkUITouchPoint, MAX_POINTS> points;
|
||||
ConvertIMMEventToTouchEvent(info, rawInputEvent, points);
|
||||
eventData.gestureAsyncEvent.rawPointerEvent = &rawInputEvent;
|
||||
SendArkUIAsyncEvent(&eventData);
|
||||
SendGestureEvent(info, static_cast<int32_t>(ON_ACTION_START), extraParam);
|
||||
};
|
||||
gestureRef->SetOnActionId(onActionAccept);
|
||||
gestureRef->SetOnActionStartId(onActionAccept);
|
||||
}
|
||||
if (actionTypeMask & ARKUI_GESTURE_EVENT_ACTION_UPDATE) {
|
||||
auto onActionUpdate = [extraParam](GestureEvent& info) {
|
||||
ArkUINodeEvent eventData;
|
||||
eventData.kind = GESTURE_ASYNC_EVENT;
|
||||
eventData.nodeId = 0;
|
||||
eventData.extraParam = reinterpret_cast<ArkUI_Int64>(extraParam);
|
||||
eventData.gestureAsyncEvent.subKind = ON_ACTION_UPDATE;
|
||||
GetGestureEvent(eventData.gestureAsyncEvent, info);
|
||||
ArkUITouchEvent rawInputEvent;
|
||||
std::array<ArkUITouchPoint, MAX_POINTS> points;
|
||||
ConvertIMMEventToTouchEvent(info, rawInputEvent, points);
|
||||
eventData.gestureAsyncEvent.rawPointerEvent = &rawInputEvent;
|
||||
SendArkUIAsyncEvent(&eventData);
|
||||
SendGestureEvent(info, static_cast<int32_t>(ON_ACTION_UPDATE), extraParam);
|
||||
};
|
||||
gestureRef->SetOnActionUpdateId(onActionUpdate);
|
||||
}
|
||||
if (actionTypeMask & ARKUI_GESTURE_EVENT_ACTION_END) {
|
||||
auto onActionEnd = [extraParam](GestureEvent& info) {
|
||||
ArkUINodeEvent eventData;
|
||||
eventData.kind = GESTURE_ASYNC_EVENT;
|
||||
eventData.nodeId = 0;
|
||||
eventData.extraParam = reinterpret_cast<ArkUI_Int64>(extraParam);
|
||||
eventData.gestureAsyncEvent.subKind = ON_ACTION_END;
|
||||
GetGestureEvent(eventData.gestureAsyncEvent, info);
|
||||
ArkUITouchEvent rawInputEvent;
|
||||
std::array<ArkUITouchPoint, MAX_POINTS> points;
|
||||
ConvertIMMEventToTouchEvent(info, rawInputEvent, points);
|
||||
eventData.gestureAsyncEvent.rawPointerEvent = &rawInputEvent;
|
||||
SendArkUIAsyncEvent(&eventData);
|
||||
SendGestureEvent(info, static_cast<int32_t>(ON_ACTION_END), extraParam);
|
||||
};
|
||||
gestureRef->SetOnActionEndId(onActionEnd);
|
||||
}
|
||||
|
@ -17,7 +17,10 @@
|
||||
#define FRAMEWORKS_CORE_INTERFACES_NATIVE_NODE_TOUCH_EVENT_CONVERTOR_H
|
||||
|
||||
#include "frameworks/core/event/touch_event.h"
|
||||
#include "frameworks/core/event/mouse_event.h"
|
||||
|
||||
namespace OHOS::Ace::NG {
|
||||
TouchEvent ConvertToTouchEvent(const std::shared_ptr<MMI::PointerEvent>& srcPointerEvent);
|
||||
void ConvertToMouseEvent(MouseEvent& event, const std::shared_ptr<MMI::PointerEvent>& srcPointerEvent);
|
||||
}
|
||||
#endif
|
@ -74,6 +74,13 @@ int32_t OH_ArkUI_UIInputEvent_GetAction(const ArkUI_UIInputEvent* event)
|
||||
}
|
||||
return OHOS::Ace::NodeModel::ConvertToCTouchActionType(touchEvent->action);
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent) {
|
||||
return -1;
|
||||
}
|
||||
return OHOS::Ace::NodeModel::ConvertToCMouseActionType(mouseEvent->action);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -93,6 +100,13 @@ int32_t OH_ArkUI_UIInputEvent_GetSourceType(const ArkUI_UIInputEvent* event)
|
||||
}
|
||||
return touchEvent->sourceType;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent) {
|
||||
return static_cast<int32_t>(UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN);
|
||||
}
|
||||
return mouseEvent->sourceType;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -112,6 +126,13 @@ int32_t OH_ArkUI_UIInputEvent_GetToolType(const ArkUI_UIInputEvent* event)
|
||||
}
|
||||
return OHOS::Ace::NodeModel::ConvertToCInputEventToolType(touchEvent->actionTouchPoint.toolType);
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent) {
|
||||
return static_cast<int32_t>(UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN);
|
||||
}
|
||||
return OHOS::Ace::NodeModel::ConvertToCInputEventToolType(mouseEvent->actionTouchPoint.toolType);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -150,7 +171,7 @@ int64_t OH_ArkUI_UIInputEvent_GetEventTime(const ArkUI_UIInputEvent* event)
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent) {
|
||||
return 0.0f;
|
||||
return 0;
|
||||
}
|
||||
return mouseEvent->timeStamp;
|
||||
}
|
||||
@ -173,6 +194,13 @@ uint32_t OH_ArkUI_PointerEvent_GetPointerCount(const ArkUI_UIInputEvent* event)
|
||||
}
|
||||
return touchEvent->touchPointSize;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -192,6 +220,13 @@ int32_t OH_ArkUI_PointerEvent_GetPointerId(const ArkUI_UIInputEvent* event, uint
|
||||
}
|
||||
return touchEvent->touchPointes[pointerIndex].id;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.id;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -251,6 +286,13 @@ float OH_ArkUI_PointerEvent_GetXByIndex(const ArkUI_UIInputEvent* event, uint32_
|
||||
}
|
||||
return touchEvent->touchPointes[pointerIndex].nodeX;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.nodeX;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -311,6 +353,13 @@ float OH_ArkUI_PointerEvent_GetYByIndex(const ArkUI_UIInputEvent* event, uint32_
|
||||
}
|
||||
return touchEvent->touchPointes[pointerIndex].nodeY;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.nodeY;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -371,6 +420,13 @@ float OH_ArkUI_PointerEvent_GetWindowXByIndex(const ArkUI_UIInputEvent* event, u
|
||||
}
|
||||
return touchEvent->touchPointes[pointerIndex].windowX;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.windowX;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -431,6 +487,13 @@ float OH_ArkUI_PointerEvent_GetWindowYByIndex(const ArkUI_UIInputEvent* event, u
|
||||
}
|
||||
return touchEvent->touchPointes[pointerIndex].windowY;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.windowY;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -491,6 +554,13 @@ float OH_ArkUI_PointerEvent_GetDisplayXByIndex(const ArkUI_UIInputEvent* event,
|
||||
}
|
||||
return touchEvent->touchPointes[pointerIndex].screenX;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.screenX;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -551,6 +621,13 @@ float OH_ArkUI_PointerEvent_GetDisplayYByIndex(const ArkUI_UIInputEvent* event,
|
||||
}
|
||||
return touchEvent->touchPointes[pointerIndex].screenY;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.screenY;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -570,6 +647,13 @@ float OH_ArkUI_PointerEvent_GetPressure(const ArkUI_UIInputEvent* event, uint32_
|
||||
}
|
||||
return touchEvent->touchPointes[touchEvent->touchPointSize-1].pressure;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.pressure;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -589,6 +673,13 @@ float OH_ArkUI_PointerEvent_GetTiltX(const ArkUI_UIInputEvent* event, uint32_t p
|
||||
}
|
||||
return touchEvent->touchPointes[touchEvent->touchPointSize-1].tiltX;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.tiltX;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -608,6 +699,13 @@ float OH_ArkUI_PointerEvent_GetTiltY(const ArkUI_UIInputEvent* event, uint32_t p
|
||||
}
|
||||
return touchEvent->touchPointes[touchEvent->touchPointSize-1].tiltY;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.tiltY;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -627,6 +725,13 @@ float OH_ArkUI_PointerEvent_GetTouchAreaWidth(const ArkUI_UIInputEvent* event, u
|
||||
}
|
||||
return touchEvent->touchPointes[touchEvent->touchPointSize-1].contactAreaWidth;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.contactAreaWidth;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -646,6 +751,13 @@ float OH_ArkUI_PointerEvent_GetTouchAreaHeight(const ArkUI_UIInputEvent* event,
|
||||
}
|
||||
return touchEvent->touchPointes[touchEvent->touchPointSize-1].contactAreaHeight;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
const auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent || pointerIndex != 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
return mouseEvent->actionTouchPoint.contactAreaHeight;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1017,6 +1129,13 @@ int32_t OH_ArkUI_PointerEvent_SetInterceptHitTestMode(const ArkUI_UIInputEvent*
|
||||
touchEvent->interceptResult = static_cast<int32_t>(mode);
|
||||
break;
|
||||
}
|
||||
case C_MOUSE_EVENT_ID: {
|
||||
auto* mouseEvent = reinterpret_cast<ArkUIMouseEvent*>(event->inputEvent);
|
||||
if (!mouseEvent) {
|
||||
return OHOS::Ace::ERROR_CODE_PARAM_INVALID;
|
||||
}
|
||||
return mouseEvent->interceptResult = static_cast<int32_t>(mode);
|
||||
}
|
||||
default:
|
||||
return OHOS::Ace::ERROR_CODE_PARAM_INVALID;
|
||||
}
|
||||
|
@ -333,8 +333,13 @@ void HandleGestureEvent(ArkUINodeEvent* event)
|
||||
return;
|
||||
}
|
||||
ArkUI_UIInputEvent uiEvent;
|
||||
uiEvent.inputType = ARKUI_UIINPUTEVENT_TYPE_TOUCH;
|
||||
uiEvent.eventTypeId = C_TOUCH_EVENT_ID;
|
||||
if (gestureEvent->eventData.source == static_cast<int32_t>(UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE)) {
|
||||
uiEvent.eventTypeId = C_MOUSE_EVENT_ID;
|
||||
uiEvent.inputType = ARKUI_UIINPUTEVENT_TYPE_MOUSE;
|
||||
} else {
|
||||
uiEvent.eventTypeId = C_TOUCH_EVENT_ID;
|
||||
uiEvent.inputType = ARKUI_UIINPUTEVENT_TYPE_TOUCH;
|
||||
}
|
||||
uiEvent.inputEvent = gestureEvent->eventData.rawPointerEvent;
|
||||
gestureEvent->eventData.rawPointerEvent = &uiEvent;
|
||||
extraData->targetReceiver(gestureEvent, extraData->extraParam);
|
||||
|
Loading…
Reference in New Issue
Block a user