use just one event listener per applies-to widget in usercss editor
This commit is contained in:
parent
c723ec58ce
commit
489546e35c
|
@ -1,79 +1,12 @@
|
||||||
/* global regExpTester debounce messageBox CodeMirror */
|
/* global regExpTester debounce messageBox CodeMirror template */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function templateCache(cache) {
|
|
||||||
function clone(id) {
|
|
||||||
if (typeof cache[id] === 'function') {
|
|
||||||
cache[id] = cache[id]();
|
|
||||||
}
|
|
||||||
return cache[id].cloneNode(true);
|
|
||||||
}
|
|
||||||
return {clone};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createAppliesToLineWidget(cm) {
|
function createAppliesToLineWidget(cm) {
|
||||||
const APPLIES_TYPE = [
|
|
||||||
[t('appliesUrlOption'), 'url'],
|
|
||||||
[t('appliesUrlPrefixOption'), 'url-prefix'],
|
|
||||||
[t('appliesDomainOption'), 'domain'],
|
|
||||||
[t('appliesRegexpOption'), 'regexp']
|
|
||||||
];
|
|
||||||
const THROTTLE_DELAY = 400;
|
const THROTTLE_DELAY = 400;
|
||||||
|
let TPL, EVENTS, CLICK_ROUTE;
|
||||||
let widgets = [];
|
let widgets = [];
|
||||||
let fromLine, toLine, styleVariables;
|
let fromLine, toLine, styleVariables;
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
|
|
||||||
const template = templateCache({
|
|
||||||
container: () =>
|
|
||||||
$element({className: 'applies-to', appendChild: [
|
|
||||||
$element({tag: 'label', appendChild: t('appliesLabel')}),
|
|
||||||
$element({
|
|
||||||
tag: 'ul',
|
|
||||||
className: 'applies-to-list'
|
|
||||||
})
|
|
||||||
]}),
|
|
||||||
listItem: () =>
|
|
||||||
$element({tag: 'li', appendChild: [
|
|
||||||
$element({
|
|
||||||
tag: 'select',
|
|
||||||
className: 'applies-type',
|
|
||||||
appendChild: APPLIES_TYPE.map(([label, value]) => $element({
|
|
||||||
tag: 'option',
|
|
||||||
value: value,
|
|
||||||
textContent: label
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
$element({
|
|
||||||
tag: 'input',
|
|
||||||
className: 'applies-value'
|
|
||||||
}),
|
|
||||||
$element({
|
|
||||||
tag: 'button',
|
|
||||||
type: 'button',
|
|
||||||
className: 'applies-to-regexp-test',
|
|
||||||
textContent: t('styleRegexpTestButton')
|
|
||||||
}),
|
|
||||||
$element({
|
|
||||||
tag: 'button',
|
|
||||||
type: 'button',
|
|
||||||
className: 'applies-to-remove',
|
|
||||||
textContent: t('appliesRemove')
|
|
||||||
}),
|
|
||||||
$element({
|
|
||||||
tag: 'button',
|
|
||||||
type: 'button',
|
|
||||||
className: 'applies-to-add',
|
|
||||||
textContent: t('appliesAdd')
|
|
||||||
})
|
|
||||||
]}),
|
|
||||||
appliesToEverything: () =>
|
|
||||||
$element({
|
|
||||||
tag: 'li',
|
|
||||||
className: 'applies-to-everything',
|
|
||||||
textContent: t('appliesToEverything')
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
return {toggle};
|
return {toggle};
|
||||||
|
|
||||||
function toggle(newState = !initialized) {
|
function toggle(newState = !initialized) {
|
||||||
|
@ -90,6 +23,147 @@ function createAppliesToLineWidget(cm) {
|
||||||
function init() {
|
function init() {
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|
||||||
|
TPL = {
|
||||||
|
container:
|
||||||
|
$element({className: 'applies-to', appendChild: [
|
||||||
|
$element({tag: 'label', appendChild: t('appliesLabel')}),
|
||||||
|
$element({tag: 'ul', className: 'applies-to-list'}),
|
||||||
|
]}),
|
||||||
|
listItem: $element({
|
||||||
|
tag: 'li',
|
||||||
|
className: 'applies-to-item',
|
||||||
|
appendChild: [
|
||||||
|
$element({
|
||||||
|
tag: 'select',
|
||||||
|
className: 'applies-type',
|
||||||
|
appendChild: [
|
||||||
|
[t('appliesUrlOption'), 'url'],
|
||||||
|
[t('appliesUrlPrefixOption'), 'url-prefix'],
|
||||||
|
[t('appliesDomainOption'), 'domain'],
|
||||||
|
[t('appliesRegexpOption'), 'regexp']
|
||||||
|
].map(([textContent, value]) => $element({
|
||||||
|
tag: 'option',
|
||||||
|
value,
|
||||||
|
textContent,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
$element({
|
||||||
|
tag: 'input',
|
||||||
|
className: 'applies-value',
|
||||||
|
}),
|
||||||
|
$element({
|
||||||
|
tag: 'button',
|
||||||
|
className: 'test-regexp',
|
||||||
|
textContent: t('styleRegexpTestButton'),
|
||||||
|
}),
|
||||||
|
$element({
|
||||||
|
tag: 'button',
|
||||||
|
className: 'remove-applies-to',
|
||||||
|
textContent: t('appliesRemove'),
|
||||||
|
}),
|
||||||
|
$element({
|
||||||
|
tag: 'button',
|
||||||
|
className: 'add-applies-to',
|
||||||
|
textContent: t('appliesAdd'),
|
||||||
|
})
|
||||||
|
]}),
|
||||||
|
appliesToEverything: $element({
|
||||||
|
tag: 'li',
|
||||||
|
className: 'applies-to-everything',
|
||||||
|
textContent: t('appliesToEverything'),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
CLICK_ROUTE = {
|
||||||
|
'.test-regexp': (item, apply) => {
|
||||||
|
regExpTester.toggle();
|
||||||
|
regExpTester.update([apply.value.text]);
|
||||||
|
},
|
||||||
|
|
||||||
|
'.remove-applies-to': (item, apply) => {
|
||||||
|
const applies = item.closest('.applies-to').__applies;
|
||||||
|
const i = applies.indexOf(apply);
|
||||||
|
let repl;
|
||||||
|
let from;
|
||||||
|
let to;
|
||||||
|
if (applies.length < 2) {
|
||||||
|
messageBox({
|
||||||
|
contents: t('appliesRemoveError'),
|
||||||
|
buttons: [t('confirmClose')]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (i === 0) {
|
||||||
|
from = apply.mark.find().from;
|
||||||
|
to = applies[i + 1].mark.find().from;
|
||||||
|
repl = '';
|
||||||
|
} else if (i === applies.length - 1) {
|
||||||
|
from = applies[i - 1].mark.find().to;
|
||||||
|
to = apply.mark.find().to;
|
||||||
|
repl = '';
|
||||||
|
} else {
|
||||||
|
from = applies[i - 1].mark.find().to;
|
||||||
|
to = applies[i + 1].mark.find().from;
|
||||||
|
repl = ', ';
|
||||||
|
}
|
||||||
|
cm.replaceRange(repl, from, to, 'appliesTo');
|
||||||
|
clearApply(apply);
|
||||||
|
item.remove();
|
||||||
|
applies.splice(i, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
'.add-applies-to': (item, apply) => {
|
||||||
|
const applies = this.closest('.applies-to').__applies;
|
||||||
|
const i = applies.indexOf(apply);
|
||||||
|
const pos = apply.mark.find().to;
|
||||||
|
const text = `, ${apply.type.text}("")`;
|
||||||
|
cm.replaceRange(text, pos, pos, 'appliesTo');
|
||||||
|
const newApply = createApply(
|
||||||
|
cm.indexFromPos(pos) + 2,
|
||||||
|
apply.type.text,
|
||||||
|
'',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
setupApplyMarkers(newApply);
|
||||||
|
applies.splice(i + 1, 0, newApply);
|
||||||
|
item.insertAdjacentElement('afterend', buildChildren(applies, newApply));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
EVENTS = {
|
||||||
|
onchange({target}) {
|
||||||
|
const typeElement = target.closest('.applies-type');
|
||||||
|
if (typeElement) {
|
||||||
|
const item = target.closest('.applies-to-item');
|
||||||
|
const apply = item.__apply;
|
||||||
|
changeItem(apply, 'type', typeElement.value);
|
||||||
|
item.dataset.type = apply.type.text;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
oninput({target}) {
|
||||||
|
if (target.matches('.applies-value')) {
|
||||||
|
const apply = target.closest('.applies-to-item').__apply;
|
||||||
|
debounce(changeItem, THROTTLE_DELAY, apply, 'value', target.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onfocus({target}) {
|
||||||
|
if (target.matches('.test-regexp')) {
|
||||||
|
const apply = target.closest('.applies-to-item').__apply;
|
||||||
|
updateRegexpTest(apply);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onclick({target}) {
|
||||||
|
for (const selector in CLICK_ROUTE) {
|
||||||
|
const routed = target.closest(selector);
|
||||||
|
if (routed) {
|
||||||
|
const item = routed.closest('.applies-to-item');
|
||||||
|
CLICK_ROUTE[selector].call(routed, item, item.__apply);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
styleVariables = $element({tag: 'style'});
|
styleVariables = $element({tag: 'style'});
|
||||||
fromLine = 0;
|
fromLine = 0;
|
||||||
toLine = cm.doc.size;
|
toLine = cm.doc.size;
|
||||||
|
@ -175,7 +249,7 @@ function createAppliesToLineWidget(cm) {
|
||||||
inOp = true;
|
inOp = true;
|
||||||
cm.startOperation();
|
cm.startOperation();
|
||||||
}
|
}
|
||||||
cm.operation(doUpdate);
|
doUpdate();
|
||||||
}
|
}
|
||||||
if (inOp) {
|
if (inOp) {
|
||||||
cm.endOperation();
|
cm.endOperation();
|
||||||
|
@ -245,26 +319,29 @@ function createAppliesToLineWidget(cm) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let itemHeight;
|
let itemHeight;
|
||||||
for (const section of findAppliesTo(start, end)) {
|
for (const section of findAppliesTo(start, end)) {
|
||||||
while (removed[i] && removed[i].line.lineNo() < section.pos.line) {
|
let removedWidget = removed[i];
|
||||||
clearWidget(removed[i++]);
|
while (removedWidget && removedWidget.line.lineNo() < section.pos.line) {
|
||||||
|
clearWidget(removed[i]);
|
||||||
|
removedWidget = removed[++i];
|
||||||
}
|
}
|
||||||
for (const a of section.applies) {
|
for (const a of section.applies) {
|
||||||
setupApplyMarkers(a, lineIndexes);
|
setupApplyMarkers(a, lineIndexes);
|
||||||
}
|
}
|
||||||
if (removed[i] && removed[i].line.lineNo() === section.pos.line) {
|
if (removedWidget && removedWidget.line.lineNo() === section.pos.line) {
|
||||||
// reuse old widget
|
// reuse old widget
|
||||||
removed[i].section.applies.forEach(apply => {
|
removedWidget.section.applies.forEach(apply => {
|
||||||
apply.type.mark.clear();
|
apply.type.mark.clear();
|
||||||
apply.value.mark.clear();
|
apply.value.mark.clear();
|
||||||
});
|
});
|
||||||
removed[i].section = section;
|
removedWidget.section = section;
|
||||||
const newNode = buildElement(section);
|
const newNode = buildElement(section);
|
||||||
if (removed[i].node.parentNode) {
|
const removedNode = removedWidget.node;
|
||||||
removed[i].node.parentNode.replaceChild(newNode, removed[i].node);
|
if (removedNode.parentNode) {
|
||||||
|
removedNode.parentNode.replaceChild(newNode, removedNode);
|
||||||
}
|
}
|
||||||
removed[i].node = newNode;
|
removedWidget.node = newNode;
|
||||||
removed[i].changed();
|
removedWidget.changed();
|
||||||
yield removed[i];
|
yield removedWidget;
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -337,128 +414,55 @@ function createAppliesToLineWidget(cm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildElement({applies}) {
|
function buildElement({applies}) {
|
||||||
const el = template.clone('container');
|
const container = TPL.container.cloneNode(true);
|
||||||
const appliesToList = $('.applies-to-list', el);
|
const list = $('.applies-to-list', container);
|
||||||
applies.map(makeLi)
|
for (const apply of applies) {
|
||||||
.forEach(item => appliesToList.appendChild(item));
|
list.appendChild(buildChildren(applies, apply));
|
||||||
if (!appliesToList.childNodes.length) {
|
}
|
||||||
appliesToList.appendChild(template.clone('appliesToEverything'));
|
if (!list.children[0]) {
|
||||||
|
list.appendChild(TPL.appliesToEverything.cloneNode(true));
|
||||||
|
}
|
||||||
|
return Object.assign(container, EVENTS, {__applies: applies});
|
||||||
}
|
}
|
||||||
return el;
|
|
||||||
|
|
||||||
function makeLi(apply) {
|
function buildChildren(applies, apply) {
|
||||||
const el = template.clone('listItem');
|
const el = TPL.listItem.cloneNode(true);
|
||||||
el.dataset.type = apply.type.text;
|
el.dataset.type = apply.type.text;
|
||||||
el.addEventListener('change', e => {
|
el.__apply = apply;
|
||||||
if (e.target.classList.contains('applies-type')) {
|
$('.applies-type', el).value = apply.type.text;
|
||||||
el.dataset.type = apply.type.text;
|
$('.applies-value', el).value = apply.value.text;
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const typeInput = $('.applies-type', el);
|
|
||||||
typeInput.value = apply.type.text;
|
|
||||||
typeInput.onchange = function () {
|
|
||||||
applyChange(apply.type, this.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const valueInput = $('.applies-value', el);
|
|
||||||
valueInput.value = apply.value.text;
|
|
||||||
valueInput.oninput = function () {
|
|
||||||
debounce(applyChange, THROTTLE_DELAY, apply.value, this.value);
|
|
||||||
};
|
|
||||||
valueInput.onfocus = updateRegexpTest;
|
|
||||||
|
|
||||||
const regexpTestButton = $('.applies-to-regexp-test', el);
|
|
||||||
regexpTestButton.onclick = () => {
|
|
||||||
regExpTester.toggle();
|
|
||||||
regExpTester.update([apply.value.text]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeButton = $('.applies-to-remove', el);
|
|
||||||
removeButton.onclick = function () {
|
|
||||||
const i = applies.indexOf(apply);
|
|
||||||
let repl;
|
|
||||||
let from;
|
|
||||||
let to;
|
|
||||||
if (applies.length < 2) {
|
|
||||||
messageBox({
|
|
||||||
contents: chrome.i18n.getMessage('appliesRemoveError'),
|
|
||||||
buttons: [t('confirmClose')]
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (i === 0) {
|
|
||||||
from = apply.mark.find().from;
|
|
||||||
to = applies[i + 1].mark.find().from;
|
|
||||||
repl = '';
|
|
||||||
} else if (i === applies.length - 1) {
|
|
||||||
from = applies[i - 1].mark.find().to;
|
|
||||||
to = apply.mark.find().to;
|
|
||||||
repl = '';
|
|
||||||
} else {
|
|
||||||
from = applies[i - 1].mark.find().to;
|
|
||||||
to = applies[i + 1].mark.find().from;
|
|
||||||
repl = ', ';
|
|
||||||
}
|
|
||||||
cm.replaceRange(repl, from, to, 'appliesTo');
|
|
||||||
clearApply(apply);
|
|
||||||
this.closest('li').remove();
|
|
||||||
applies.splice(i, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addButton = $('.applies-to-add', el);
|
|
||||||
addButton.onclick = function () {
|
|
||||||
const i = applies.indexOf(apply);
|
|
||||||
const pos = apply.mark.find().to;
|
|
||||||
const text = `, ${apply.type.text}("")`;
|
|
||||||
cm.replaceRange(text, pos, pos, 'appliesTo');
|
|
||||||
const newApply = createApply(
|
|
||||||
cm.indexFromPos(pos) + 2,
|
|
||||||
apply.type.text,
|
|
||||||
'',
|
|
||||||
true
|
|
||||||
);
|
|
||||||
setupApplyMarkers(newApply);
|
|
||||||
applies.splice(i + 1, 0, newApply);
|
|
||||||
this.closest('li').insertAdjacentElement('afterend', makeLi(newApply));
|
|
||||||
};
|
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
|
|
||||||
function updateRegexpTest() {
|
|
||||||
if (apply.type.text === 'regexp') {
|
|
||||||
const re = apply.value.text.trim();
|
|
||||||
if (re) {
|
|
||||||
regExpTester.update([re]);
|
|
||||||
} else {
|
|
||||||
regExpTester.update([]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyChange(input, newText) {
|
function changeItem(apply, part, newText) {
|
||||||
const range = input.mark.find();
|
part = apply[part];
|
||||||
input.mark.clear();
|
const range = part.mark.find();
|
||||||
|
part.mark.clear();
|
||||||
cm.replaceRange(newText, range.from, range.to, 'appliesTo');
|
cm.replaceRange(newText, range.from, range.to, 'appliesTo');
|
||||||
input.mark = cm.markText(
|
part.mark = cm.markText(
|
||||||
range.from,
|
range.from,
|
||||||
cm.findPosH(range.from, newText.length, 'char'),
|
cm.findPosH(range.from, newText.length, 'char'),
|
||||||
{clearWhenEmpty: false}
|
{clearWhenEmpty: false}
|
||||||
);
|
);
|
||||||
input.text = newText;
|
part.text = newText;
|
||||||
|
|
||||||
if (input === apply.type) {
|
if (part === apply.type) {
|
||||||
const range = apply.mark.find();
|
const range = apply.mark.find();
|
||||||
apply.mark.clear();
|
apply.mark.clear();
|
||||||
apply.mark = cm.markText(
|
apply.mark = cm.markText(
|
||||||
input.mark.find().from,
|
part.mark.find().from,
|
||||||
range.to,
|
range.to,
|
||||||
{clearWhenEmpty: false}
|
{clearWhenEmpty: false}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRegexpTest();
|
updateRegexpTest(apply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateRegexpTest(apply) {
|
||||||
|
if (apply.type.text === 'regexp') {
|
||||||
|
const rx = apply.value.text.trim();
|
||||||
|
regExpTester.update(rx ? [rx] : {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,8 +491,12 @@ function createAppliesToLineWidget(cm) {
|
||||||
|
|
||||||
function *findAppliesTo(posStart, posEnd) {
|
function *findAppliesTo(posStart, posEnd) {
|
||||||
const text = cm.getValue();
|
const text = cm.getValue();
|
||||||
const re = /^[\t ]*@-moz-document\s+/mg;
|
const re = /^[\t ]*@-moz-document[\s\n]+/gm;
|
||||||
const applyRe = /(url|url-prefix|domain|regexp)\(((['"])(?:\\\\|\\\n|\\\3|[^\n])*?\3|[^)\n]*)\)[\s,]*/iyg;
|
const applyRe = new RegExp([
|
||||||
|
/(?:\/\*[^*]*\*\/[\s\n]*)*/,
|
||||||
|
/(url|url-prefix|domain|regexp)/,
|
||||||
|
/\(((['"])(?:\\\\|\\\n|\\\3|[^\n])*?\3|[^)\n]*)\)\s*(,\s*)?/,
|
||||||
|
].map(rx => rx.source).join(''), 'giy');
|
||||||
let match;
|
let match;
|
||||||
re.lastIndex = posStart;
|
re.lastIndex = posStart;
|
||||||
while ((match = re.exec(text))) {
|
while ((match = re.exec(text))) {
|
||||||
|
|
|
@ -331,6 +331,7 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
|
||||||
.test-regexp {
|
.test-regexp {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.single-editor .test-regexp,
|
||||||
.has-regexp .test-regexp {
|
.has-regexp .test-regexp {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
@ -610,11 +611,15 @@ html:not(.usercss) .usercss-only,
|
||||||
margin-top: 0.35rem;
|
margin-top: 0.35rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-linewidget .applies-to li:not([data-type="regexp"]) .applies-to-regexp-test {
|
.CodeMirror-linewidget .applies-to li:not([data-type="regexp"]) .test-regexp {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-linewidget li.applies-to-everything {
|
.CodeMirror-linewidget li .add-applies-to {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-linewidget .applies-to-everything {
|
||||||
margin-top: 0.2rem;
|
margin-top: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,17 +25,17 @@ var regExpTester = (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isShowed() {
|
function isShown() {
|
||||||
return Boolean($('.regexp-report'));
|
return Boolean($('.regexp-report'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(state = !isShowed()) {
|
function toggle(state = !isShown()) {
|
||||||
if (state && !isShowed()) {
|
if (state && !isShown()) {
|
||||||
if (!isInit) {
|
if (!isInit) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
showHelp('', $element({className: 'regexp-report'}));
|
showHelp('', $element({className: 'regexp-report'}));
|
||||||
} else if (!state && isShowed()) {
|
} else if (!state && isShown()) {
|
||||||
if (isInit) {
|
if (isInit) {
|
||||||
uninit();
|
uninit();
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ var regExpTester = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function update(newRegexps) {
|
function update(newRegexps) {
|
||||||
if (!isShowed()) {
|
if (!isShown()) {
|
||||||
if (isInit) {
|
if (isInit) {
|
||||||
uninit();
|
uninit();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user