Added after render and render on extension with child handlers #26
This commit is contained in:
+14
-4
@@ -20,12 +20,20 @@ impl Div {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Div {
|
impl Element for Div {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
||||||
scope.use_area(width, height);
|
scope.use_area(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn after_render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
ctx: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let mut transform = scope.get_transform().clone();
|
let mut transform = scope.get_transform().clone();
|
||||||
let (w, h) = scope.get_parent_size();
|
let (w, h) = scope.get_parent_size();
|
||||||
scope.set_parent_size(transform.width, transform.height);
|
scope.set_parent_size(transform.width, transform.height);
|
||||||
@@ -43,7 +51,8 @@ impl Element for Div {
|
|||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
elem.get_elem().render(scope);
|
elem.get_elem().render(scope, ctx);
|
||||||
|
ctx.render(elem, scope);
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
@@ -54,7 +63,8 @@ impl Element for Div {
|
|||||||
t.y += transform.y + transform.py;
|
t.y += transform.y + transform.py;
|
||||||
|
|
||||||
scope.draw();
|
scope.draw();
|
||||||
elem.get_elem().after_render(scope);
|
elem.get_elem().after_render(scope, ctx);
|
||||||
|
ctx.after_render(elem, scope);
|
||||||
}
|
}
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
self.size = (transform.width, transform.height);
|
self.size = (transform.width, transform.height);
|
||||||
|
|||||||
+28
-8
@@ -38,12 +38,20 @@ impl FlexCol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for FlexRow {
|
impl Element for FlexRow {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
||||||
scope.use_area(width, height);
|
scope.use_area(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn after_render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
ctx: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let mut transform = scope.get_transform().clone();
|
let mut transform = scope.get_transform().clone();
|
||||||
let (w, h) = scope.get_parent_size();
|
let (w, h) = scope.get_parent_size();
|
||||||
scope.set_parent_size(transform.width, transform.height);
|
scope.set_parent_size(transform.width, transform.height);
|
||||||
@@ -62,7 +70,8 @@ impl Element for FlexRow {
|
|||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
elem.get_elem().render(scope);
|
elem.get_elem().render(scope, ctx);
|
||||||
|
ctx.render(elem, scope);
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
@@ -77,7 +86,8 @@ impl Element for FlexRow {
|
|||||||
|
|
||||||
scope.draw();
|
scope.draw();
|
||||||
|
|
||||||
elem.get_elem().after_render(scope);
|
elem.get_elem().after_render(scope, ctx);
|
||||||
|
ctx.after_render(elem, scope);
|
||||||
}
|
}
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
self.size = (transform.width, transform.height);
|
self.size = (transform.width, transform.height);
|
||||||
@@ -98,12 +108,20 @@ impl Element for FlexRow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for FlexCol {
|
impl Element for FlexCol {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
||||||
scope.use_area(width, height);
|
scope.use_area(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn after_render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
ctx: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let mut transform = scope.get_transform().clone();
|
let mut transform = scope.get_transform().clone();
|
||||||
let (w, h) = scope.get_parent_size();
|
let (w, h) = scope.get_parent_size();
|
||||||
scope.set_parent_size(transform.width, transform.height);
|
scope.set_parent_size(transform.width, transform.height);
|
||||||
@@ -122,7 +140,8 @@ impl Element for FlexCol {
|
|||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
elem.get_elem().render(scope);
|
elem.get_elem().render(scope, ctx);
|
||||||
|
ctx.render(elem, scope);
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
@@ -137,7 +156,8 @@ impl Element for FlexCol {
|
|||||||
|
|
||||||
scope.draw();
|
scope.draw();
|
||||||
|
|
||||||
elem.get_elem().after_render(scope);
|
elem.get_elem().after_render(scope, ctx);
|
||||||
|
ctx.after_render(elem, scope);
|
||||||
}
|
}
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
self.size = (transform.width, transform.height);
|
self.size = (transform.width, transform.height);
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ impl Heading {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Heading {
|
impl Element for Heading {
|
||||||
fn render(&mut self, scope: &mut crate::prelude::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::prelude::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
for element in &self.children {
|
for element in &self.children {
|
||||||
if element.get::<NoRender>().is_some() {
|
if element.get::<NoRender>().is_some() {
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ pub struct Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Input {
|
impl Element for Input {
|
||||||
fn render(&mut self, scope: &mut crate::prelude::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::prelude::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let s = self.state.get();
|
let s = self.state.get();
|
||||||
scope.draw_text(0, 0, &s);
|
scope.draw_text(0, 0, &s);
|
||||||
if let Some(c) = s.chars().nth(self.cursor) {
|
if let Some(c) = s.chars().nth(self.cursor) {
|
||||||
|
|||||||
+5
-15
@@ -13,7 +13,11 @@ pub use paginator::*;
|
|||||||
use crate::Element;
|
use crate::Element;
|
||||||
|
|
||||||
impl Element for String {
|
impl Element for String {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
scope.draw_text(0, 0, self);
|
scope.draw_text(0, 0, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,17 +29,3 @@ impl Element for String {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for (String, u32) {
|
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
|
||||||
scope.draw_text_colored(0, 0, &self.0, self.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,12 +24,20 @@ impl Paginator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Paginator {
|
impl Element for Paginator {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
||||||
scope.use_area(width, height);
|
scope.use_area(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn after_render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
ctx: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
if let Some(elem) = self.children.get(self.index) {
|
if let Some(elem) = self.children.get(self.index) {
|
||||||
if elem.get::<NoRender>().is_some() {
|
if elem.get::<NoRender>().is_some() {
|
||||||
return;
|
return;
|
||||||
@@ -46,7 +54,7 @@ impl Element for Paginator {
|
|||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
elem.get_elem().render(scope);
|
elem.get_elem().render(scope, ctx);
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
@@ -57,7 +65,8 @@ impl Element for Paginator {
|
|||||||
t.y += transform.y + transform.py;
|
t.y += transform.y + transform.py;
|
||||||
|
|
||||||
scope.draw();
|
scope.draw();
|
||||||
elem.get_elem().after_render(scope);
|
elem.get_elem().after_render(scope, ctx);
|
||||||
|
ctx.after_render(elem, scope);
|
||||||
|
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
self.size = (transform.width, transform.height);
|
self.size = (transform.width, transform.height);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use crate::extensions::{Extension, ExtensionContext};
|
use crate::extensions::{Extension, Context};
|
||||||
|
|
||||||
pub struct InputExtension;
|
pub struct InputExtension;
|
||||||
|
|
||||||
impl Extension for InputExtension {
|
impl Extension for InputExtension {
|
||||||
fn init(&mut self, ctx: &ExtensionContext) {
|
fn init(&mut self, ctx: &Context) {
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
crossterm::terminal::enable_raw_mode().unwrap();
|
crossterm::terminal::enable_raw_mode().unwrap();
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || loop {
|
||||||
|
|||||||
+24
-5
@@ -21,10 +21,17 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub trait Extension {
|
pub trait Extension {
|
||||||
fn init(&mut self, _ctx: &ExtensionContext) {}
|
fn init(&mut self, _ctx: &Context) {}
|
||||||
fn event(&mut self, _ctx: &ExtensionContext, _event: &dyn Event) {}
|
fn event(&mut self, _ctx: &Context, _event: &dyn Event) {}
|
||||||
fn on_close(&mut self) {}
|
fn on_close(&mut self) {}
|
||||||
fn render_widget(&mut self, _scope: &mut RenderScope, _widget: &Arc<Widget>) {}
|
fn render_widget(&mut self, _ctx: &Context, _scope: &mut RenderScope, _widget: &Arc<Widget>) {}
|
||||||
|
fn after_render_widget(
|
||||||
|
&mut self,
|
||||||
|
_ctx: &Context,
|
||||||
|
_scope: &mut RenderScope,
|
||||||
|
_widget: &Arc<Widget>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Event: Send + Sync {
|
pub trait Event: Send + Sync {
|
||||||
@@ -32,7 +39,7 @@ pub trait Event: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExtensionContext {
|
pub struct Context {
|
||||||
screen: Arc<Screen>,
|
screen: Arc<Screen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +78,7 @@ impl<'a> dyn Event + 'a {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtensionContext {
|
impl Context {
|
||||||
pub fn new(screen: Arc<Screen>) -> Self {
|
pub fn new(screen: Arc<Screen>) -> Self {
|
||||||
Self { screen }
|
Self { screen }
|
||||||
}
|
}
|
||||||
@@ -108,4 +115,16 @@ impl ExtensionContext {
|
|||||||
}
|
}
|
||||||
components
|
components
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, w: &Arc<Widget>, scope: &mut RenderScope) {
|
||||||
|
for ext in self.screen.extensions.lock().unwrap().iter() {
|
||||||
|
ext.lock().unwrap().render_widget(self, scope, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn after_render(&self, w: &Arc<Widget>, scope: &mut RenderScope) {
|
||||||
|
for ext in self.screen.extensions.lock().unwrap().iter() {
|
||||||
|
ext.lock().unwrap().after_render_widget(self, scope, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::event;
|
use crate::event;
|
||||||
use crate::extensions::{Extension, ExtensionContext};
|
use crate::extensions::{Context, Extension};
|
||||||
|
|
||||||
pub struct TickExtension(pub u16);
|
pub struct TickExtension(pub u16);
|
||||||
|
|
||||||
event!(TickEvent(pub u32));
|
event!(TickEvent(pub u32));
|
||||||
|
|
||||||
impl Extension for TickExtension {
|
impl Extension for TickExtension {
|
||||||
fn init(&mut self, ctx: &ExtensionContext) {
|
fn init(&mut self, ctx: &Context) {
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
let rate_dur = 1000 / self.0 as u64;
|
let rate_dur = 1000 / self.0 as u64;
|
||||||
std::thread::spawn({
|
std::thread::spawn({
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl VelocityExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Extension for VelocityExtension {
|
impl Extension for VelocityExtension {
|
||||||
fn init(&mut self, ctx: &super::ExtensionContext) {
|
fn init(&mut self, ctx: &super::Context) {
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
std::thread::spawn({
|
std::thread::spawn({
|
||||||
move || {
|
move || {
|
||||||
|
|||||||
+7
-10
@@ -33,7 +33,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
extensions::{Extension, Handler},
|
extensions::{Extension, Handler},
|
||||||
prelude::ExtensionContext,
|
prelude::Context,
|
||||||
render_scope::RenderScope,
|
render_scope::RenderScope,
|
||||||
widget::{BoxedElement, DynWidget, Element, StaticWidget, Widget, WidgetLoad},
|
widget::{BoxedElement, DynWidget, Element, StaticWidget, Widget, WidgetLoad},
|
||||||
};
|
};
|
||||||
@@ -159,7 +159,7 @@ impl Screen {
|
|||||||
///
|
///
|
||||||
/// This method blocks and repeatedly renders the screen at a fixed interval.
|
/// This method blocks and repeatedly renders the screen at a fixed interval.
|
||||||
pub fn run(self: &Arc<Self>) -> std::io::Result<()> {
|
pub fn run(self: &Arc<Self>) -> std::io::Result<()> {
|
||||||
let ctx = ExtensionContext::new(self.clone());
|
let ctx = Context::new(self.clone());
|
||||||
|
|
||||||
for ext in self.extensions.lock().unwrap().iter() {
|
for ext in self.extensions.lock().unwrap().iter() {
|
||||||
ext.lock().unwrap().init(&ctx);
|
ext.lock().unwrap().init(&ctx);
|
||||||
@@ -168,7 +168,7 @@ impl Screen {
|
|||||||
utils::hide_cursor()?;
|
utils::hide_cursor()?;
|
||||||
|
|
||||||
while *self.running.lock().unwrap() {
|
while *self.running.lock().unwrap() {
|
||||||
self.render()?;
|
self.render(&ctx)?;
|
||||||
std::thread::sleep(std::time::Duration::from_millis(28));
|
std::thread::sleep(std::time::Duration::from_millis(28));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ impl Screen {
|
|||||||
/// Renders all widgets and applies extensions.
|
/// Renders all widgets and applies extensions.
|
||||||
///
|
///
|
||||||
/// This method is called internally by `run`.
|
/// This method is called internally by `run`.
|
||||||
pub fn render(self: &Arc<Self>) -> std::io::Result<()> {
|
pub fn render(self: &Arc<Self>, ctx: &Context) -> std::io::Result<()> {
|
||||||
let mut scope = RenderScope::new();
|
let mut scope = RenderScope::new();
|
||||||
let (w, h) = crossterm::terminal::size().unwrap();
|
let (w, h) = crossterm::terminal::size().unwrap();
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
@@ -186,9 +186,6 @@ impl Screen {
|
|||||||
utils::clear()?;
|
utils::clear()?;
|
||||||
for elem in self.widgets.lock().unwrap().iter() {
|
for elem in self.widgets.lock().unwrap().iter() {
|
||||||
if elem.get::<NoRender>().is_some() || elem.get::<NoRenderRoot>().is_some() {
|
if elem.get::<NoRender>().is_some() || elem.get::<NoRenderRoot>().is_some() {
|
||||||
for ext in self.extensions.lock().unwrap().iter() {
|
|
||||||
ext.lock().unwrap().render_widget(&mut scope, elem);
|
|
||||||
}
|
|
||||||
elem.auto_refresh();
|
elem.auto_refresh();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -206,17 +203,17 @@ impl Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ext in self.extensions.lock().unwrap().iter() {
|
for ext in self.extensions.lock().unwrap().iter() {
|
||||||
ext.lock().unwrap().render_widget(&mut scope, elem);
|
ext.lock().unwrap().render_widget(ctx, &mut scope, elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.get_elem().render(&mut scope);
|
elem.get_elem().render(&mut scope, ctx);
|
||||||
|
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
scope.draw();
|
scope.draw();
|
||||||
|
|
||||||
elem.get_elem().after_render(&mut scope);
|
elem.get_elem().after_render(&mut scope, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.auto_refresh();
|
elem.auto_refresh();
|
||||||
|
|||||||
+2
-2
@@ -31,11 +31,11 @@ pub type BoxedComponent = Box<dyn Component + Send + Sync>;
|
|||||||
pub trait Element: Send + Sync {
|
pub trait Element: Send + Sync {
|
||||||
/// Called to perform rendering for the element.
|
/// Called to perform rendering for the element.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn render(&mut self, scope: &mut RenderScope) {}
|
fn render(&mut self, scope: &mut RenderScope, ctx: &crate::extensions::Context) {}
|
||||||
|
|
||||||
/// Called after rendering, for follow-up logic or cleanup.
|
/// Called after rendering, for follow-up logic or cleanup.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn after_render(&mut self, scope: &mut RenderScope) {}
|
fn after_render(&mut self, scope: &mut RenderScope, ctx: &crate::extensions::Context) {}
|
||||||
|
|
||||||
/// Called to draw child widgets, if any.
|
/// Called to draw child widgets, if any.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
|||||||
Reference in New Issue
Block a user