Updating settings through backend API should work now. Fixed issues with setting the listening address.
This commit is contained in:
parent
ce85d00619
commit
5f4e8985f2
13
package-lock.json
generated
13
package-lock.json
generated
@ -10,6 +10,7 @@
|
|||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
|
"tailwind-scrollbar": "^3.1.0",
|
||||||
"tailwindcss": "^3.4.1"
|
"tailwindcss": "^3.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1287,6 +1288,18 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwind-scrollbar": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-pmrtDIZeHyu2idTejfV59SbaJyvp1VRjYxAjZBH0jnyrPRo6HL1kD5Glz8VPagasqr6oAx6M05+Tuw429Z8jxg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.13.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tailwindcss": "3.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.1",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
|
"tailwind-scrollbar": "^3.1.0",
|
||||||
"tailwindcss": "^3.4.1"
|
"tailwindcss": "^3.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,11 @@ from json import loads
|
|||||||
|
|
||||||
|
|
||||||
class ServerAddressForm(forms.Form):
|
class ServerAddressForm(forms.Form):
|
||||||
# with open('tracker/static/tracker/data/listen_address.json', 'r') as address:
|
|
||||||
# read_address = loads(address.read())
|
|
||||||
# if (read_address['address']['user'] != ""):
|
|
||||||
# server_address = read_address['address']['user']
|
|
||||||
# else:
|
|
||||||
# server_address = read_address['address']['default']
|
|
||||||
server_address_form = forms.URLField(
|
server_address_form = forms.URLField(
|
||||||
max_length=1312, initial="http://localhost:8000/spoiler", empty_value="http://localhost:8000/spoiler", label="")
|
max_length=1312, initial="http://localhost:8000/", empty_value="http://localhost:8000/", label="")
|
||||||
server_address_form.widget.attrs["class"] = "w-full text-sm rounded-md border-2 border-bluelight-dark bg-[#242424]/10"
|
server_address_form.widget.attrs["class"] = "w-full text-sm rounded-md border-2 border-bluelight-dark bg-[#242424]/10"
|
||||||
|
|
||||||
|
|
||||||
|
class BackendFilepathForm(forms.Form):
|
||||||
|
backend_filepath_form = forms.CharField()
|
||||||
|
backend_filepath_form.widget.attrs["class"] = "w-full text-sm rounded-md border-2 border-bluelight-dark bg-[#242424]/10"
|
||||||
|
@ -20,25 +20,58 @@ window.onload = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function get_updated_server_address() {
|
async function get_updated_filepath() {
|
||||||
try {
|
fetch(`${document.URL}get/settings`)
|
||||||
fetch(`${document.URL}get/address`)
|
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then(
|
.then(
|
||||||
(data) => {
|
(data) => {
|
||||||
server_address = JSON.parse(JSON.stringify(data));
|
backend_filepath = JSON.parse(JSON.stringify(data));
|
||||||
|
fetch(`${server_address}settings`, {
|
||||||
|
method: "post",
|
||||||
|
headers: { "Content-type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
secretLegend: backend_filepath,
|
||||||
|
address: ":8000",
|
||||||
|
}),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch {
|
}
|
||||||
|
|
||||||
|
// This won't work properly. For now, the user will need to input themselves.
|
||||||
|
// async function get_filepath() {
|
||||||
|
// try {
|
||||||
|
// const filepath_selection = await window.showDirectoryPicker();
|
||||||
|
// console.log(filepath_selection);
|
||||||
|
// } catch {
|
||||||
|
// console.log("Browser does not support showDirectoryPicker()");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
async function get_updated_server_address() {
|
||||||
|
fetch(`${document.URL}get/address`)
|
||||||
|
.then(
|
||||||
|
(response) => response.json(),
|
||||||
|
(error) => {
|
||||||
console.log("Are you sure the front end is up?");
|
console.log("Are you sure the front end is up?");
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(data) => {
|
||||||
|
server_address = JSON.parse(JSON.stringify(data));
|
||||||
|
get_updated_filepath();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refresh_elements(cross_codes) {
|
async function refresh_elements(cross_codes) {
|
||||||
fetch(server_address)
|
fetch(`${server_address}spoiler`)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then(
|
.then(
|
||||||
(data) => {
|
(data) => {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Tunic Tracker Redux</title>
|
<title>Tunic Transition Tracker</title>
|
||||||
<meta name="description" content="Tunic Transition Tracker">
|
<meta name="description" content="Tunic Transition Tracker">
|
||||||
<meta name="keywords" content="HTML, CSS, JavaScript">
|
<meta name="keywords" content="HTML, CSS, JavaScript">
|
||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
<form action="{% url "set_address" %}"
|
<form action="{% url "set_address" %}"
|
||||||
method="post"
|
method="post"
|
||||||
class="px-2 py-1 flex flex-row space-x-2 rounded-md ring-4 ring-bluelight-dark bg-bluelight-translucent"
|
class="flex flex-row space-x-2">
|
||||||
id="server-address-form">
|
<div class="w-full text-sm rounded-md border-2 border-bluelight-dark bg-[#242424]/10"
|
||||||
<div class="w-full text-sm rounded-md border-2 border-bluelight-dark bg-[#242424]/10" hidden></div>
|
hidden></div>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<label for="address-form-input"
|
<label for="server-address-form"
|
||||||
class="m-auto text-md justify-center align-top min-w-fit text-md text-nowrap">Server:</label>
|
class="m-auto text-md justify-center align-top min-w-fit text-md text-nowrap">Backend</label>
|
||||||
{% for field in form %}{{ field }}{% endfor %}
|
{% for field in server_address_form %}{{ field }}{% endfor %}
|
||||||
<input type="submit"
|
<input type="submit"
|
||||||
value="Submit"
|
value="Submit"
|
||||||
class="p-2 m-auto text-sm rounded-md ring-2 ring-bluelight-dark bg-bluelight-translucent" />
|
class="p-2 m-auto text-sm rounded-md ring-2 ring-bluelight-dark bg-bluelight-translucent"
|
||||||
|
id="server-address-form" />
|
||||||
</form>
|
</form>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<hr class="border-2 border-bluelight-translucent-dark rounded-md" />
|
<hr class="border-2 border-bluelight-translucent-dark rounded-md" />
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-4 flex flex-col space-y-2 overflow-x-scroll breakdown-block-checks-list">
|
<div class="pb-4 flex flex-col space-y-2 overflow-x-scroll scrollbar scrollbar-thumb-bluelight-dark scrollbar-track-bluelight breakdown-block-checks-list">
|
||||||
<ul class="min-w-max bg-bluelight-translucent rounded-md px-1 hidden">
|
<ul class="min-w-max bg-bluelight-translucent rounded-md px-1 hidden">
|
||||||
</ul>
|
</ul>
|
||||||
{% for check_name, check in scene_data.Checks.items %}
|
{% for check_name, check in scene_data.Checks.items %}
|
||||||
@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<hr class="border-2 border-bluelight-translucent-dark rounded-md" />
|
<hr class="border-2 border-bluelight-translucent-dark rounded-md" />
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-4 flex flex-col space-y-2 overflow-x-scroll breakdown-block-entrances-list">
|
<div class="pb-4 flex flex-col space-y-2 overflow-x-scroll scrollbar scrollbar-thumb-bluelight-dark scrollbar-track-bluelight breakdown-block-entrances-list">
|
||||||
<ul class="min-w-max bg-bluelight-translucent rounded-md px-1 hidden">
|
<ul class="min-w-max bg-bluelight-translucent rounded-md px-1 hidden">
|
||||||
</ul>
|
</ul>
|
||||||
{% for entrance_origin, entrance_destination in scene_data.Entrances.items %}
|
{% for entrance_origin, entrance_destination in scene_data.Entrances.items %}
|
||||||
@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-x-scroll">
|
<div class="overflow-x-scroll scrollbar scrollbar-thumb-bluelight-dark scrollbar-track-bluelight">
|
||||||
<div class="flex flex-col space-y-2 mx-auto min-w-max breakdown-block-mapped-list">
|
<div class="flex flex-col space-y-2 mx-auto min-w-max breakdown-block-mapped-list">
|
||||||
<ul class="bg-bluelight rounded-md px-1 hidden">
|
<ul class="bg-bluelight rounded-md px-1 hidden">
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="flex flex-col max-w-full text-lg rounded-md ring-4 bg-bluelight-translucent ring-bluelight-dark justify-start p-4 {% if current_scene.title != scene_title %}hidden{% endif %} codes-block">
|
<div class="p-4 flex flex-col space-y-2 max-w-full text-lg rounded-md ring-4 bg-bluelight-translucent ring-bluelight-dark justify-start {% if current_scene.title != scene_title %}hidden{% endif %} codes-block">
|
||||||
Holy Cross Codes
|
<div class="text-md">Holy Cross Codes</div>
|
||||||
<div class="flex flex-col space-y-2">
|
<div class="flex flex-col space-y-2">
|
||||||
<ul class="flex flex-col space-y-4 rounded-md codes-list">
|
<ul class="flex flex-col space-y-4 rounded-md codes-list">
|
||||||
{% for name, code in default_codes.items %}
|
{% for name, code in default_codes.items %}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<div class="m-4 ring-4 rounded-md text-[#eee] bg-bluelight-translucent-dark ring-bluelight-dark">
|
<div class="m-4 ring-4 rounded-md text-[#eee] bg-bluelight-translucent-dark ring-bluelight-dark">
|
||||||
{% if debug != '' %}
|
{% if debug != '' %}
|
||||||
<div class="p-4 flex flex-col space-y-4">
|
<div class="p-4 flex flex-col space-y-4">
|
||||||
<div class="flex pt-4 text-2xl mx-2">
|
<h1 class="flex text-2xl mx-2">
|
||||||
<span class="relative inline-block align-middle h-8 w-8">
|
<span class="relative inline-block align-middle h-8 w-8">
|
||||||
<img src="{% static 'tracker/images/neofox_flag_trans_256.png' %}"
|
<img src="{% static 'tracker/images/neofox_flag_trans_256.png' %}"
|
||||||
alt="A trans pride fox emoji."
|
alt="A trans pride fox emoji."
|
||||||
@ -13,9 +13,15 @@
|
|||||||
height="" />
|
height="" />
|
||||||
</span>
|
</span>
|
||||||
Tunic Transition Tracker
|
Tunic Transition Tracker
|
||||||
</div>
|
</h1>
|
||||||
<hr class="border-2 border-bluelight-translucent-dark rounded-md" />
|
<hr class="border-2 border-bluelight-translucent-dark rounded-md" />
|
||||||
|
<details class="group flex flex-col rounded-md ring-4 bg-bluelight-translucent ring-bluelight-dark">
|
||||||
|
<summary class="justify-start p-2 text-lg">Settings</summary>
|
||||||
|
<div class="p-2 flex flex-col space-y-2">
|
||||||
|
{% include "tracker/settings/index.html" %}
|
||||||
{% include "tracker/address/index.html" %}
|
{% include "tracker/address/index.html" %}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
{% include "tracker/status/index.html" %}
|
{% include "tracker/status/index.html" %}
|
||||||
<div class="p-4 flex flex-col max-w-full space-y-4 md:space-y-4 text-md rounded-md ring-4 bg-bluelight-translucent ring-bluelight-dark"
|
<div class="p-4 flex flex-col max-w-full space-y-4 md:space-y-4 text-md rounded-md ring-4 bg-bluelight-translucent ring-bluelight-dark"
|
||||||
id="overview">
|
id="overview">
|
||||||
|
14
tunictracker/tracker/templates/tracker/settings/index.html
Normal file
14
tunictracker/tracker/templates/tracker/settings/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<form action="{% url "set_settings" %}"
|
||||||
|
method="post"
|
||||||
|
class="flex flex-row space-x-2">
|
||||||
|
<div class="w-full text-sm rounded-md border-2 border-bluelight-dark bg-[#242424]/10"
|
||||||
|
hidden></div>
|
||||||
|
{% csrf_token %}
|
||||||
|
<label for="backend-filepath-form"
|
||||||
|
class="m-auto text-md justify-center align-top min-w-fit text-md text-nowrap">AppData Path</label>
|
||||||
|
{% for field in backend_filepath_form %}{{ field }}{% endfor %}
|
||||||
|
<input type="submit"
|
||||||
|
value="Submit"
|
||||||
|
class="p-2 m-auto text-sm rounded-md ring-2 ring-bluelight-dark bg-bluelight-translucent"
|
||||||
|
id="backend-filepath-form" />
|
||||||
|
</form>
|
@ -6,4 +6,6 @@ urlpatterns = [
|
|||||||
path("", views.index, name="index"),
|
path("", views.index, name="index"),
|
||||||
path("set/address/", views.set_address, name="set_address"),
|
path("set/address/", views.set_address, name="set_address"),
|
||||||
path("get/address/", views.get_address, name="get_address"),
|
path("get/address/", views.get_address, name="get_address"),
|
||||||
|
path("set/settings/", views.set_settings, name="set_settings"),
|
||||||
|
path("get/settings/", views.get_settings, name="get_settings"),
|
||||||
]
|
]
|
@ -4,39 +4,41 @@ from django.template import loader
|
|||||||
from json import loads, dumps
|
from json import loads, dumps
|
||||||
from math import floor
|
from math import floor
|
||||||
import requests
|
import requests
|
||||||
from .forms import ServerAddressForm
|
from .forms import ServerAddressForm, BackendFilepathForm
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
default_address = 'http://localhost:8000/spoiler'
|
|
||||||
|
|
||||||
|
# Debug and defaults.
|
||||||
|
defaults = {
|
||||||
|
'listen_address': 'http://localhost:8000/',
|
||||||
|
'backend_filepath': ''
|
||||||
|
}
|
||||||
|
# default_address = 'http://localhost:8000/'
|
||||||
# logging.basicConfig(encoding='utf-8', level=logging.DEBUG)
|
# logging.basicConfig(encoding='utf-8', level=logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
def session_key(session, key):
|
||||||
|
if key not in session.keys():
|
||||||
|
session[key] = defaults[key]
|
||||||
|
|
||||||
|
# TODO: consider serializing user sessions for debugging in the future.
|
||||||
|
logging.info(
|
||||||
|
f'Session {key} set to: {session[key]}')
|
||||||
|
return session[key]
|
||||||
|
|
||||||
|
is_hidden = True
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
request_data = None
|
request_data = None
|
||||||
|
|
||||||
# with open('tracker/static/tracker/data/listen_address.json', 'r') as address:
|
listen_address = session_key(request.session, 'listen_address')
|
||||||
# read_address = loads(address.read())
|
backend_filepath = session_key(request.session, 'backend_filepath')
|
||||||
# if (read_address['address']['user'] != ''):
|
|
||||||
# server_address = read_address['address']['user']
|
|
||||||
# else:
|
|
||||||
# server_address = read_address['address']['default']
|
|
||||||
if 'listen_address' in request.session.keys():
|
|
||||||
server_address = request.session['listen_address']
|
|
||||||
|
|
||||||
# TODO: consider serializing user sessions for debugging in the future.
|
|
||||||
logging.info(
|
|
||||||
f'Session listen address changed to: {server_address}')
|
|
||||||
else:
|
|
||||||
request.session['listen_address'] = default_address
|
|
||||||
server_address = default_address
|
|
||||||
logging.info(
|
|
||||||
f'Session listen address for {request.session} set to: {server_address}')
|
|
||||||
is_hidden = True
|
|
||||||
try:
|
try:
|
||||||
request_data = requests.get(
|
request_data = requests.get(
|
||||||
server_address, timeout=5, verify=True).text
|
f'{server_address}spoiler', timeout=5, verify=True).text
|
||||||
except:
|
except:
|
||||||
with open('empty_spoiler.json', 'r') as t:
|
with open('empty_spoiler.json', 'r') as t:
|
||||||
try:
|
try:
|
||||||
@ -75,10 +77,13 @@ def index(request):
|
|||||||
current_cross_codes = {}
|
current_cross_codes = {}
|
||||||
# print(e)
|
# print(e)
|
||||||
template = loader.get_template('tracker/index.html')
|
template = loader.get_template('tracker/index.html')
|
||||||
form = ServerAddressForm()
|
server_address_form = ServerAddressForm()
|
||||||
form.fields['server_address_form'].initial = server_address
|
server_address_form.fields['server_address_form'].initial = listen_address
|
||||||
|
backend_filepath_form = BackendFilepathForm()
|
||||||
|
backend_filepath_form.fields['backend_filepath_form'].initial = backend_filepath
|
||||||
context = {
|
context = {
|
||||||
'server_address': server_address,
|
'backend_filepath': backend_filepath,
|
||||||
|
'server_address': listen_address,
|
||||||
'is_hidden': is_hidden,
|
'is_hidden': is_hidden,
|
||||||
'debug': tracker_debug,
|
'debug': tracker_debug,
|
||||||
'default_codes': cross_codes['Default'],
|
'default_codes': cross_codes['Default'],
|
||||||
@ -89,7 +94,8 @@ def index(request):
|
|||||||
'title': tracker_current_scene,
|
'title': tracker_current_scene,
|
||||||
'data': tracker_current_scene_data
|
'data': tracker_current_scene_data
|
||||||
},
|
},
|
||||||
'form': form
|
'server_address_form': server_address_form,
|
||||||
|
'backend_filepath_form': backend_filepath_form
|
||||||
}
|
}
|
||||||
return HttpResponse(template.render(context, request))
|
return HttpResponse(template.render(context, request))
|
||||||
|
|
||||||
@ -104,17 +110,27 @@ def get_address(request):
|
|||||||
def set_address(request):
|
def set_address(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = ServerAddressForm(request.POST)
|
form = ServerAddressForm(request.POST)
|
||||||
form.label_classes = ('p-2')
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
# with open('tracker/static/tracker/data/listen_address.json', 'r') as la:
|
|
||||||
# listen_address = loads(la.read())
|
|
||||||
# listen_address['address']['user'] = form.cleaned_data['server_address_form']
|
|
||||||
|
|
||||||
# with open('tracker/static/tracker/data/listen_address.json', 'w') as la:
|
|
||||||
# la.write(dumps(listen_address))
|
|
||||||
server_address = form.cleaned_data['server_address_form']
|
|
||||||
request.session['listen_address'] = form.cleaned_data['server_address_form']
|
request.session['listen_address'] = form.cleaned_data['server_address_form']
|
||||||
return HttpResponseRedirect('/')
|
return HttpResponseRedirect('/')
|
||||||
else:
|
else:
|
||||||
form = ServerAddressForm()
|
form = ServerAddressForm()
|
||||||
return render(request, 'tracker/index.html', {'form': form})
|
return render(request, 'tracker/index.html', {'server_address_form': form})
|
||||||
|
|
||||||
|
|
||||||
|
def set_settings(request):
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = BackendFilepathForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
request.session['backend_filepath'] = form.cleaned_data['backend_filepath_form']
|
||||||
|
return HttpResponseRedirect('/')
|
||||||
|
else:
|
||||||
|
form = BackendFilepathForm()
|
||||||
|
return render(request, 'tracker/index.html', {'backend_filepath_form': form})
|
||||||
|
|
||||||
|
|
||||||
|
def get_settings(request):
|
||||||
|
if request.method == 'GET':
|
||||||
|
return HttpResponse(dumps(request.session['backend_filepath']), content_type="application/json")
|
||||||
|
else:
|
||||||
|
return render(request, 'tracker/index.html')
|
@ -92,5 +92,6 @@ module.exports = {
|
|||||||
require("@tailwindcss/forms"),
|
require("@tailwindcss/forms"),
|
||||||
require("@tailwindcss/typography"),
|
require("@tailwindcss/typography"),
|
||||||
require("@tailwindcss/aspect-ratio"),
|
require("@tailwindcss/aspect-ratio"),
|
||||||
|
require("tailwind-scrollbar")
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user