split project with app & doric module
This commit is contained in:
260
doric-Qt/example/doric/plugin/DoricModalPlugin.cpp
Normal file
260
doric-Qt/example/doric/plugin/DoricModalPlugin.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
#include "DoricModalPlugin.h"
|
||||
#include "engine/DoricPromise.h"
|
||||
#include "shader/DoricRootNode.h"
|
||||
#include "utils/DoricLayouts.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QObject>
|
||||
#include <QQmlComponent>
|
||||
#include <QQuickWindow>
|
||||
#include <QTimer>
|
||||
|
||||
void DoricModalPlugin::toast(QString jsValueString, QString callbackId) {
|
||||
getContext()->getDriver()->asyncCall(
|
||||
[this, jsValueString] {
|
||||
QJsonDocument document =
|
||||
QJsonDocument::fromJson(jsValueString.toUtf8());
|
||||
QJsonValue jsValue = document.object();
|
||||
|
||||
QString msg = jsValue["msg"].toString();
|
||||
int gravity = jsValue["gravity"].toInt();
|
||||
|
||||
QQmlComponent component(getContext()->getQmlEngine());
|
||||
const QUrl url(QStringLiteral("qrc:/doric/qml/toast.qml"));
|
||||
component.loadUrl(url);
|
||||
if (component.isError()) {
|
||||
qCritical() << component.errorString();
|
||||
}
|
||||
QQuickWindow *window = qobject_cast<QQuickWindow *>(component.create());
|
||||
|
||||
QQuickWindow *parentWindow =
|
||||
getContext()->getRootNode()->getRootView()->window();
|
||||
|
||||
window->contentItem()
|
||||
->childItems()
|
||||
.at(0)
|
||||
->childItems()
|
||||
.at(0)
|
||||
->childItems()
|
||||
.at(0)
|
||||
->setProperty("text", msg);
|
||||
|
||||
std::function<void()> setX = [window, parentWindow]() {
|
||||
window->setProperty("x",
|
||||
(parentWindow->width() - window->width()) / 2.f +
|
||||
parentWindow->x());
|
||||
};
|
||||
std::function<void()> setY = [window, parentWindow, gravity]() {
|
||||
if ((gravity & DoricGravity::DoricGravityBottom) ==
|
||||
DoricGravity::DoricGravityBottom) {
|
||||
window->setProperty("y", parentWindow->height() - window->height() -
|
||||
20 + parentWindow->y());
|
||||
} else if ((gravity & DoricGravity::DoricGravityTop) ==
|
||||
DoricGravity::DoricGravityTop) {
|
||||
window->setProperty("y", 20 + parentWindow->y());
|
||||
} else {
|
||||
window->setProperty(
|
||||
"y", (parentWindow->height() - window->height()) / 2 +
|
||||
parentWindow->y());
|
||||
}
|
||||
};
|
||||
// init set x
|
||||
setX();
|
||||
// init set y
|
||||
setY();
|
||||
|
||||
// update x
|
||||
connect(window, &QQuickWindow::widthChanged, setX);
|
||||
|
||||
// update y
|
||||
connect(window, &QQuickWindow::heightChanged, setY);
|
||||
|
||||
QTimer::singleShot(2000, qApp, [window]() { window->deleteLater(); });
|
||||
},
|
||||
DoricThreadMode::UI);
|
||||
}
|
||||
|
||||
void DoricModalPlugin::alert(QString jsValueString, QString callbackId) {
|
||||
getContext()->getDriver()->asyncCall(
|
||||
[this, jsValueString, callbackId] {
|
||||
QJsonDocument document =
|
||||
QJsonDocument::fromJson(jsValueString.toUtf8());
|
||||
QJsonValue jsValue = document.object();
|
||||
|
||||
QJsonValue titleVal = jsValue["title"];
|
||||
QJsonValue msgVal = jsValue["msg"];
|
||||
QJsonValue okBtn = jsValue["okLabel"];
|
||||
|
||||
QQmlComponent component(getContext()->getQmlEngine());
|
||||
const QUrl url(QStringLiteral("qrc:/doric/qml/alert.qml"));
|
||||
component.loadUrl(url);
|
||||
if (component.isError()) {
|
||||
qCritical() << component.errorString();
|
||||
}
|
||||
QQuickWindow *window = qobject_cast<QQuickWindow *>(component.create());
|
||||
window->setProperty("pointer", QString::number((qint64)window));
|
||||
window->setProperty("plugin", QString::number((qint64)this));
|
||||
window->setProperty("callbackId", callbackId);
|
||||
|
||||
window->setProperty("title", titleVal.toString());
|
||||
window->setProperty("msg", msgVal.toString());
|
||||
window->setProperty("okLabel", okBtn.toString());
|
||||
|
||||
QQuickWindow *parentWindow =
|
||||
getContext()->getRootNode()->getRootView()->window();
|
||||
|
||||
std::function<void()> setX = [window, parentWindow]() {
|
||||
window->setProperty("x",
|
||||
(parentWindow->width() - window->width()) / 2.f +
|
||||
parentWindow->x());
|
||||
};
|
||||
std::function<void()> setY = [window, parentWindow]() {
|
||||
window->setProperty("y",
|
||||
(parentWindow->height() - window->height()) / 2 +
|
||||
parentWindow->y());
|
||||
};
|
||||
// init set x
|
||||
setX();
|
||||
// init set y
|
||||
setY();
|
||||
|
||||
// update x
|
||||
connect(window, &QQuickWindow::widthChanged, setX);
|
||||
|
||||
// update y
|
||||
connect(window, &QQuickWindow::heightChanged, setY);
|
||||
},
|
||||
DoricThreadMode::UI);
|
||||
}
|
||||
|
||||
void DoricModalPlugin::confirm(QString jsValueString, QString callbackId) {
|
||||
getContext()->getDriver()->asyncCall(
|
||||
[this, jsValueString, callbackId] {
|
||||
QJsonDocument document =
|
||||
QJsonDocument::fromJson(jsValueString.toUtf8());
|
||||
QJsonValue jsValue = document.object();
|
||||
|
||||
QJsonValue titleVal = jsValue["title"];
|
||||
QJsonValue msgVal = jsValue["msg"];
|
||||
QJsonValue okBtn = jsValue["okLabel"];
|
||||
QJsonValue cancelBtn = jsValue["cancelLabel"];
|
||||
|
||||
QQmlComponent component(getContext()->getQmlEngine());
|
||||
const QUrl url(QStringLiteral("qrc:/doric/qml/confirm.qml"));
|
||||
component.loadUrl(url);
|
||||
if (component.isError()) {
|
||||
qCritical() << component.errorString();
|
||||
}
|
||||
QQuickWindow *window = qobject_cast<QQuickWindow *>(component.create());
|
||||
window->setProperty("pointer", QString::number((qint64)window));
|
||||
window->setProperty("plugin", QString::number((qint64)this));
|
||||
window->setProperty("callbackId", callbackId);
|
||||
|
||||
window->setProperty("title", titleVal.toString());
|
||||
window->setProperty("msg", msgVal.toString());
|
||||
window->setProperty("okLabel", okBtn.toString());
|
||||
window->setProperty("cancelLabel", cancelBtn.toString());
|
||||
|
||||
QQuickWindow *parentWindow =
|
||||
getContext()->getRootNode()->getRootView()->window();
|
||||
|
||||
std::function<void()> setX = [window, parentWindow]() {
|
||||
window->setProperty("x",
|
||||
(parentWindow->width() - window->width()) / 2.f +
|
||||
parentWindow->x());
|
||||
};
|
||||
std::function<void()> setY = [window, parentWindow]() {
|
||||
window->setProperty("y",
|
||||
(parentWindow->height() - window->height()) / 2 +
|
||||
parentWindow->y());
|
||||
};
|
||||
// init set x
|
||||
setX();
|
||||
// init set y
|
||||
setY();
|
||||
|
||||
// update x
|
||||
connect(window, &QQuickWindow::widthChanged, setX);
|
||||
|
||||
// update y
|
||||
connect(window, &QQuickWindow::heightChanged, setY);
|
||||
},
|
||||
DoricThreadMode::UI);
|
||||
}
|
||||
|
||||
void DoricModalPlugin::prompt(QString jsValueString, QString callbackId) {
|
||||
getContext()->getDriver()->asyncCall(
|
||||
[this, jsValueString, callbackId] {
|
||||
QJsonDocument document =
|
||||
QJsonDocument::fromJson(jsValueString.toUtf8());
|
||||
QJsonValue jsValue = document.object();
|
||||
|
||||
QJsonValue titleVal = jsValue["title"];
|
||||
QJsonValue msgVal = jsValue["msg"];
|
||||
QJsonValue okBtn = jsValue["okLabel"];
|
||||
QJsonValue cancelBtn = jsValue["cancelLabel"];
|
||||
|
||||
QQmlComponent component(getContext()->getQmlEngine());
|
||||
const QUrl url(QStringLiteral("qrc:/doric/qml/prompt.qml"));
|
||||
component.loadUrl(url);
|
||||
if (component.isError()) {
|
||||
qCritical() << component.errorString();
|
||||
}
|
||||
QQuickWindow *window = qobject_cast<QQuickWindow *>(component.create());
|
||||
window->setProperty("pointer", QString::number((qint64)window));
|
||||
window->setProperty("plugin", QString::number((qint64)this));
|
||||
window->setProperty("callbackId", callbackId);
|
||||
|
||||
window->setProperty("title", titleVal.toString());
|
||||
window->setProperty("msg", msgVal.toString());
|
||||
window->setProperty("okLabel", okBtn.toString());
|
||||
window->setProperty("cancelLabel", cancelBtn.toString());
|
||||
|
||||
QQuickWindow *parentWindow =
|
||||
getContext()->getRootNode()->getRootView()->window();
|
||||
|
||||
std::function<void()> setX = [window, parentWindow]() {
|
||||
window->setProperty("x",
|
||||
(parentWindow->width() - window->width()) / 2.f +
|
||||
parentWindow->x());
|
||||
};
|
||||
std::function<void()> setY = [window, parentWindow]() {
|
||||
window->setProperty("y",
|
||||
(parentWindow->height() - window->height()) / 2 +
|
||||
parentWindow->y());
|
||||
};
|
||||
// init set x
|
||||
setX();
|
||||
// init set y
|
||||
setY();
|
||||
|
||||
// update x
|
||||
connect(window, &QQuickWindow::widthChanged, setX);
|
||||
|
||||
// update y
|
||||
connect(window, &QQuickWindow::heightChanged, setY);
|
||||
},
|
||||
DoricThreadMode::UI);
|
||||
}
|
||||
|
||||
void DoricModalPlugin::onAccepted(QString callbackId) {
|
||||
QVariantList args;
|
||||
DoricPromise::resolve(getContext(), callbackId, args);
|
||||
}
|
||||
|
||||
void DoricModalPlugin::onAcceptedWithInput(QString callbackId, QString input) {
|
||||
QVariantList args;
|
||||
args.append(input);
|
||||
DoricPromise::resolve(getContext(), callbackId, args);
|
||||
}
|
||||
|
||||
void DoricModalPlugin::onRejected(QString callbackId) {
|
||||
QVariantList args;
|
||||
DoricPromise::reject(getContext(), callbackId, args);
|
||||
}
|
||||
|
||||
void DoricModalPlugin::onRejectedWithInput(QString callbackId, QString input) {
|
||||
QVariantList args;
|
||||
args.append(input);
|
||||
DoricPromise::reject(getContext(), callbackId, args);
|
||||
}
|
28
doric-Qt/example/doric/plugin/DoricModalPlugin.h
Normal file
28
doric-Qt/example/doric/plugin/DoricModalPlugin.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef DORICMODALPLUGIN_H
|
||||
#define DORICMODALPLUGIN_H
|
||||
|
||||
#include "DoricNativePlugin.h"
|
||||
|
||||
class DoricModalPlugin : public DoricNativePlugin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using DoricNativePlugin::DoricNativePlugin;
|
||||
|
||||
Q_INVOKABLE void toast(QString jsValueString, QString callbackId);
|
||||
|
||||
Q_INVOKABLE void alert(QString jsValueString, QString callbackId);
|
||||
|
||||
Q_INVOKABLE void confirm(QString jsValueString, QString callbackId);
|
||||
|
||||
Q_INVOKABLE void prompt(QString jsValueString, QString callbackId);
|
||||
|
||||
void onAccepted(QString callbackId);
|
||||
|
||||
void onAcceptedWithInput(QString callbackId, QString input);
|
||||
|
||||
void onRejected(QString callbackId);
|
||||
|
||||
void onRejectedWithInput(QString callbackId, QString input);
|
||||
};
|
||||
|
||||
#endif // DORICMODALPLUGIN_H
|
11
doric-Qt/example/doric/plugin/DoricNativePlugin.h
Normal file
11
doric-Qt/example/doric/plugin/DoricNativePlugin.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef DORICNATIVEPLUGIN_H
|
||||
#define DORICNATIVEPLUGIN_H
|
||||
|
||||
#include "../utils/DoricContextHolder.h"
|
||||
|
||||
class DoricNativePlugin : public DoricContextHolder {
|
||||
public:
|
||||
using DoricContextHolder::DoricContextHolder;
|
||||
};
|
||||
|
||||
#endif // DORICNATIVEPLUGIN_H
|
29
doric-Qt/example/doric/plugin/DoricNetworkPlugin.cpp
Normal file
29
doric-Qt/example/doric/plugin/DoricNetworkPlugin.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "DoricNetworkPlugin.h"
|
||||
#include "engine/DoricPromise.h"
|
||||
#include "utils/DoricNetworkService.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
void DoricNetworkPlugin::request(QString jsValueString, QString callbackId) {
|
||||
QJsonDocument document = QJsonDocument::fromJson(jsValueString.toUtf8());
|
||||
QJsonValue jsValue = document.object();
|
||||
|
||||
DoricNetworkService::getInstance()->request(
|
||||
jsValue, qApp,
|
||||
[this, callbackId](int code, QList<QByteArray> headers, QByteArray data) {
|
||||
getContext()->getDriver()->asyncCall(
|
||||
[this, callbackId, code, headers, data] {
|
||||
QMap<QString, QVariant> map;
|
||||
map.insert("status", code);
|
||||
map.insert("headers", QVariant::fromValue(headers));
|
||||
map.insert("data", QString(data));
|
||||
|
||||
QVariantList args;
|
||||
args.append(map);
|
||||
DoricPromise::resolve(getContext(), callbackId, args);
|
||||
},
|
||||
DoricThreadMode::JS);
|
||||
});
|
||||
}
|
13
doric-Qt/example/doric/plugin/DoricNetworkPlugin.h
Normal file
13
doric-Qt/example/doric/plugin/DoricNetworkPlugin.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef DORICNETWORKPLUGIN_H
|
||||
#define DORICNETWORKPLUGIN_H
|
||||
|
||||
#include "DoricNativePlugin.h"
|
||||
|
||||
class DoricNetworkPlugin : public DoricNativePlugin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using DoricNativePlugin::DoricNativePlugin;
|
||||
|
||||
Q_INVOKABLE void request(QString jsValueString, QString callbackId);
|
||||
};
|
||||
#endif // DORICNETWORKPLUGIN_H
|
120
doric-Qt/example/doric/plugin/DoricPopoverPlugin.cpp
Normal file
120
doric-Qt/example/doric/plugin/DoricPopoverPlugin.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "DoricPopoverPlugin.h"
|
||||
#include "engine/DoricPromise.h"
|
||||
#include "shader/DoricRootNode.h"
|
||||
#include "shader/DoricViewNode.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QQuickWindow>
|
||||
|
||||
void DoricPopoverPlugin::show(QString jsValueString, QString callbackId) {
|
||||
getContext()->getDriver()->asyncCall(
|
||||
[this, jsValueString, callbackId] {
|
||||
QJsonDocument document =
|
||||
QJsonDocument::fromJson(jsValueString.toUtf8());
|
||||
QJsonValue jsValue = document.object();
|
||||
|
||||
QQuickItem *rootItem =
|
||||
getContext()->getRootNode()->getRootView()->window()->contentItem();
|
||||
|
||||
if (this->fullScreenView == nullptr) {
|
||||
QQmlComponent component(getContext()->getQmlEngine());
|
||||
|
||||
const QUrl url(QStringLiteral("qrc:/doric/qml/stack.qml"));
|
||||
component.loadUrl(url);
|
||||
|
||||
if (component.isError()) {
|
||||
qCritical() << component.errorString();
|
||||
}
|
||||
|
||||
QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
|
||||
item->setWidth(rootItem->width());
|
||||
item->setHeight(rootItem->height());
|
||||
|
||||
DoricLayouts *layout = new DoricLayouts();
|
||||
layout->setWidth(item->width());
|
||||
layout->setHeight(item->height());
|
||||
layout->setView(item);
|
||||
layout->setLayoutType(DoricLayoutType::DoricStack);
|
||||
|
||||
item->setProperty("doricLayout", QString::number((qint64)layout));
|
||||
|
||||
item->setParentItem(rootItem);
|
||||
|
||||
this->fullScreenView = item;
|
||||
} else {
|
||||
DoricLayouts *layout =
|
||||
(DoricLayouts *)(this->fullScreenView->property("doricLayout")
|
||||
.toULongLong());
|
||||
layout->setWidth(rootItem->width());
|
||||
layout->setHeight(rootItem->height());
|
||||
}
|
||||
this->fullScreenView->setVisible(true);
|
||||
|
||||
QString viewId = jsValue["id"].toString();
|
||||
QString type = jsValue["type"].toString();
|
||||
|
||||
DoricViewNode *viewNode = getContext()->targetViewNode(viewId);
|
||||
if (viewNode == nullptr) {
|
||||
viewNode = DoricViewNode::create(getContext(), type);
|
||||
viewNode->setId(viewId);
|
||||
viewNode->init(nullptr);
|
||||
|
||||
viewNode->getNodeView()->setParentItem(this->fullScreenView);
|
||||
}
|
||||
|
||||
viewNode->blend(jsValue["props"]);
|
||||
|
||||
DoricLayouts *layout =
|
||||
(DoricLayouts *)(this->fullScreenView->property("doricLayout")
|
||||
.toULongLong());
|
||||
layout->apply();
|
||||
|
||||
getContext()->addHeadNode(TYPE, viewNode);
|
||||
|
||||
QVariantList args;
|
||||
DoricPromise::resolve(getContext(), callbackId, args);
|
||||
},
|
||||
DoricThreadMode::UI);
|
||||
}
|
||||
|
||||
void DoricPopoverPlugin::dismiss(QString jsValueString, QString callbackId) {
|
||||
getContext()->getDriver()->asyncCall(
|
||||
[this, jsValueString, callbackId] {
|
||||
QJsonDocument document =
|
||||
QJsonDocument::fromJson(jsValueString.toUtf8());
|
||||
QJsonValue jsValue = document.object();
|
||||
|
||||
if (jsValue.toObject().contains("id")) {
|
||||
QString viewId = jsValue["id"].toString();
|
||||
|
||||
DoricViewNode *viewNode = getContext()->targetViewNode(viewId);
|
||||
this->dismissViewNode(viewNode);
|
||||
} else {
|
||||
this->dismissPopover();
|
||||
}
|
||||
|
||||
QVariantList args;
|
||||
DoricPromise::resolve(getContext(), callbackId, args);
|
||||
},
|
||||
DoricThreadMode::UI);
|
||||
}
|
||||
|
||||
void DoricPopoverPlugin::dismissViewNode(DoricViewNode *viewNode) {
|
||||
if (viewNode != nullptr) {
|
||||
getContext()->removeHeadNode(TYPE, viewNode);
|
||||
viewNode->getNodeView()->setParent(nullptr);
|
||||
viewNode->getNodeView()->setParentItem(nullptr);
|
||||
viewNode->getNodeView()->deleteLater();
|
||||
}
|
||||
|
||||
if (getContext()->allHeadNodes(TYPE).size() == 0) {
|
||||
this->fullScreenView->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DoricPopoverPlugin::dismissPopover() {
|
||||
foreach (DoricViewNode *node, getContext()->allHeadNodes(TYPE)) {
|
||||
dismissViewNode(node);
|
||||
}
|
||||
}
|
27
doric-Qt/example/doric/plugin/DoricPopoverPlugin.h
Normal file
27
doric-Qt/example/doric/plugin/DoricPopoverPlugin.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef DORICPOPOVERPLUGIN_H
|
||||
#define DORICPOPOVERPLUGIN_H
|
||||
|
||||
#include "DoricNativePlugin.h"
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
static QString TYPE = "popover";
|
||||
|
||||
class DoricPopoverPlugin : public DoricNativePlugin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using DoricNativePlugin::DoricNativePlugin;
|
||||
|
||||
Q_INVOKABLE void show(QString jsValueString, QString callbackId);
|
||||
|
||||
Q_INVOKABLE void dismiss(QString jsValueString, QString callbackId);
|
||||
|
||||
private:
|
||||
QQuickItem *fullScreenView = nullptr;
|
||||
|
||||
void dismissViewNode(DoricViewNode *node);
|
||||
|
||||
void dismissPopover();
|
||||
};
|
||||
|
||||
#endif // DORICPOPOVERPLUGIN_H
|
36
doric-Qt/example/doric/plugin/DoricShaderPlugin.cpp
Normal file
36
doric-Qt/example/doric/plugin/DoricShaderPlugin.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "../shader/DoricRootNode.h"
|
||||
#include "DoricShaderPlugin.h"
|
||||
|
||||
void DoricShaderPlugin::render(QString jsValueString, QString callbackId) {
|
||||
getContext()->getDriver()->asyncCall(
|
||||
[this, jsValueString] {
|
||||
try {
|
||||
QJsonDocument document =
|
||||
QJsonDocument::fromJson(jsValueString.toUtf8());
|
||||
QJsonValue jsValue = document.object();
|
||||
|
||||
QString viewId = jsValue["id"].toString();
|
||||
DoricRootNode *rootNode = getContext()->getRootNode();
|
||||
|
||||
if (rootNode->getId().isEmpty() &&
|
||||
jsValue["type"].toString() == "Root") {
|
||||
rootNode->setId(viewId);
|
||||
rootNode->blend(jsValue["props"]);
|
||||
rootNode->requestLayout();
|
||||
} else {
|
||||
DoricViewNode *viewNode = getContext()->targetViewNode(viewId);
|
||||
if (viewNode != nullptr) {
|
||||
viewNode->blend(jsValue["props"]);
|
||||
viewNode->requestLayout();
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
qCritical() << "render exception";
|
||||
}
|
||||
},
|
||||
DoricThreadMode::UI);
|
||||
}
|
14
doric-Qt/example/doric/plugin/DoricShaderPlugin.h
Normal file
14
doric-Qt/example/doric/plugin/DoricShaderPlugin.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef SHADERPLUGIN_H
|
||||
#define SHADERPLUGIN_H
|
||||
|
||||
#include "DoricNativePlugin.h"
|
||||
|
||||
class DoricShaderPlugin : public DoricNativePlugin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using DoricNativePlugin::DoricNativePlugin;
|
||||
|
||||
Q_INVOKABLE void render(QString jsValueString, QString callbackId);
|
||||
};
|
||||
|
||||
#endif // SHADERPLUGIN_H
|
Reference in New Issue
Block a user