Fix rink
This commit is contained in:
8 files changed
+187
-97
No files matched your search
@@ -1,12 +1,11 @@
|
|||||||
use crate::point_interaction::{PointData, PointInteraction};
|
use crate::point_interaction::{PointData, PointInteraction};
|
||||||
use dioxus_core::Event;
|
use dioxus_core::Event;
|
||||||
use std::fmt::{Debug, Formatter};
|
|
||||||
|
|
||||||
pub type MouseEvent = Event<MouseData>;
|
pub type MouseEvent = Event<MouseData>;
|
||||||
|
|
||||||
/// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
|
/// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
/// Data associated with a mouse event
|
/// Data associated with a mouse event
|
||||||
pub struct MouseData {
|
pub struct MouseData {
|
||||||
/// Common data for all pointer/mouse events
|
/// Common data for all pointer/mouse events
|
||||||
@@ -89,14 +88,3 @@ impl PointInteraction for MouseData {
|
|||||||
self.point_data
|
self.point_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for MouseData {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("MouseData")
|
|
||||||
.field("coordinates", &self.coordinates())
|
|
||||||
.field("modifiers", &self.modifiers())
|
|
||||||
.field("held_buttons", &self.held_buttons())
|
|
||||||
.field("trigger_button", &self.trigger_button())
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use dioxus_core::Event;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
use crate::point_interaction::{PointData, PointInteraction};
|
use crate::point_interaction::{PointData, PointInteraction};
|
||||||
|
|
||||||
|
/// A synthetic event that wraps a web-style [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent)
|
||||||
pub type PointerEvent = Event<PointerData>;
|
pub type PointerEvent = Event<PointerData>;
|
||||||
|
|
||||||
|
/// Data associated with a pointer event, aside from the data shared with mouse events
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct PointerData {
|
pub struct PointerEventData {
|
||||||
/// Common data for all pointer/mouse events
|
|
||||||
#[cfg_attr(feature = "serialize", serde(flatten))]
|
|
||||||
point_data: PointData,
|
|
||||||
pub pointer_id: i32,
|
pub pointer_id: i32,
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
@@ -23,6 +23,54 @@ pub struct PointerData {
|
|||||||
pub is_primary: bool,
|
pub is_primary: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct PointerData {
|
||||||
|
/// Common data for all pointer/mouse events
|
||||||
|
#[cfg_attr(feature = "serialize", serde(flatten))]
|
||||||
|
point_data: PointData,
|
||||||
|
|
||||||
|
/// The unique identifier of the pointer causing the event.
|
||||||
|
#[deprecated(since = "0.5.0", note = "use pointer_id() instead")]
|
||||||
|
pub pointer_id: i32,
|
||||||
|
|
||||||
|
/// The width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer.
|
||||||
|
#[deprecated(since = "0.5.0", note = "use width() instead")]
|
||||||
|
pub width: i32,
|
||||||
|
|
||||||
|
/// The height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer.
|
||||||
|
#[deprecated(since = "0.5.0", note = "use height() instead")]
|
||||||
|
pub height: i32,
|
||||||
|
|
||||||
|
/// The normalized pressure of the pointer input in the range of 0 to 1,
|
||||||
|
#[deprecated(since = "0.5.0", note = "use pressure() instead")]
|
||||||
|
pub pressure: f32,
|
||||||
|
|
||||||
|
/// The normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1,
|
||||||
|
#[deprecated(since = "0.5.0", note = "use tangential_pressure() instead")]
|
||||||
|
pub tangential_pressure: f32,
|
||||||
|
|
||||||
|
/// The plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis.
|
||||||
|
#[deprecated(since = "0.5.0", note = "use tilt_x() instead")]
|
||||||
|
pub tilt_x: i32,
|
||||||
|
|
||||||
|
/// The plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis.
|
||||||
|
#[deprecated(since = "0.5.0", note = "use tilt_y() instead")]
|
||||||
|
pub tilt_y: i32,
|
||||||
|
|
||||||
|
/// The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.
|
||||||
|
#[deprecated(since = "0.5.0", note = "use twist() instead")]
|
||||||
|
pub twist: i32,
|
||||||
|
|
||||||
|
/// Indicates the device type that caused the event (mouse, pen, touch, etc.).
|
||||||
|
#[deprecated(since = "0.5.0", note = "use pointer_type() instead")]
|
||||||
|
pub pointer_type: String,
|
||||||
|
|
||||||
|
/// Indicates if the pointer represents the primary pointer of this pointer type.
|
||||||
|
#[deprecated(since = "0.5.0", note = "use is_primary() instead")]
|
||||||
|
pub is_primary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl_event![
|
impl_event![
|
||||||
PointerData;
|
PointerData;
|
||||||
/// pointerdown
|
/// pointerdown
|
||||||
@@ -56,59 +104,87 @@ impl_event![
|
|||||||
onpointerout
|
onpointerout
|
||||||
];
|
];
|
||||||
|
|
||||||
|
impl PointerData {
|
||||||
|
pub fn new(point_data: PointData, pointer_event_data: PointerEventData) -> Self {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
Self {
|
||||||
|
point_data,
|
||||||
|
pointer_id: pointer_event_data.pointer_id,
|
||||||
|
width: pointer_event_data.width,
|
||||||
|
height: pointer_event_data.height,
|
||||||
|
pressure: pointer_event_data.pressure,
|
||||||
|
tangential_pressure: pointer_event_data.tangential_pressure,
|
||||||
|
tilt_x: pointer_event_data.tilt_x,
|
||||||
|
tilt_y: pointer_event_data.tilt_y,
|
||||||
|
twist: pointer_event_data.twist,
|
||||||
|
pointer_type: pointer_event_data.pointer_type,
|
||||||
|
is_primary: pointer_event_data.is_primary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the unique identifier of the pointer causing the event.
|
||||||
|
pub fn pointer_id(&self) -> i32 {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.pointer_id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer.
|
||||||
|
pub fn width(&self) -> i32 {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer.
|
||||||
|
pub fn height(&self) -> i32 {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the normalized pressure of the pointer input in the range of 0 to 1,
|
||||||
|
pub fn pressure(&self) -> f32 {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.pressure
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1,
|
||||||
|
pub fn tangential_pressure(&self) -> f32 {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.tangential_pressure
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis.
|
||||||
|
pub fn tilt_x(&self) -> i32 {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.tilt_x
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis.
|
||||||
|
pub fn tilt_y(&self) -> i32 {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.tilt_y
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.
|
||||||
|
pub fn twist(&self) -> i32 {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.twist
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the device type that caused the event (mouse, pen, touch, etc.).
|
||||||
|
pub fn pointer_type(&self) -> &str {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.pointer_type.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets if the pointer represents the primary pointer of this pointer type.
|
||||||
|
pub fn is_primary(&self) -> bool {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
self.is_primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PointInteraction for PointerData {
|
impl PointInteraction for PointerData {
|
||||||
fn get_point_data(&self) -> PointData {
|
fn get_point_data(&self) -> PointData {
|
||||||
self.point_data
|
self.point_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointerData {
|
|
||||||
pub fn new(
|
|
||||||
point_data: PointData,
|
|
||||||
pointer_id: i32,
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
pressure: f32,
|
|
||||||
tangential_pressure: f32,
|
|
||||||
tilt_x: i32,
|
|
||||||
tilt_y: i32,
|
|
||||||
twist: i32,
|
|
||||||
pointer_type: String,
|
|
||||||
is_primary: bool,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
point_data,
|
|
||||||
pointer_id,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
pressure,
|
|
||||||
tangential_pressure,
|
|
||||||
tilt_x,
|
|
||||||
tilt_y,
|
|
||||||
twist,
|
|
||||||
pointer_type,
|
|
||||||
is_primary,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for PointerData {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("PointerData")
|
|
||||||
.field("coordinates", &self.coordinates())
|
|
||||||
.field("modifiers", &self.modifiers())
|
|
||||||
.field("held_buttons", &self.held_buttons())
|
|
||||||
.field("trigger_button", &self.trigger_button())
|
|
||||||
.field("pointer_id", &self.pointer_id)
|
|
||||||
.field("width", &self.width)
|
|
||||||
.field("height", &self.height)
|
|
||||||
.field("pressure", &self.pressure)
|
|
||||||
.field("tangential_pressure", &self.tangential_pressure)
|
|
||||||
.field("tilt_x", &self.tilt_x)
|
|
||||||
.field("tilt_y", &self.tilt_y)
|
|
||||||
.field("twist", &self.twist)
|
|
||||||
.field("pointer_type", &self.pointer_type)
|
|
||||||
.field("is_primary", &self.is_primary)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,7 @@ mod global_attributes;
|
|||||||
pub mod input_data;
|
pub mod input_data;
|
||||||
#[cfg(feature = "native-bind")]
|
#[cfg(feature = "native-bind")]
|
||||||
pub mod native_bind;
|
pub mod native_bind;
|
||||||
mod point_interaction;
|
pub mod point_interaction;
|
||||||
mod render_template;
|
mod render_template;
|
||||||
#[cfg(feature = "wasm-bind")]
|
#[cfg(feature = "wasm-bind")]
|
||||||
mod web_sys_bind;
|
mod web_sys_bind;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
use keyboard_types::Modifiers;
|
use keyboard_types::Modifiers;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -69,8 +71,8 @@ pub struct PointData {
|
|||||||
impl PointData {
|
impl PointData {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
trigger_button: Option<MouseButton>,
|
trigger_button: Option<MouseButton>,
|
||||||
coordinates: Coordinates,
|
|
||||||
held_buttons: MouseButtonSet,
|
held_buttons: MouseButtonSet,
|
||||||
|
coordinates: Coordinates,
|
||||||
modifiers: Modifiers,
|
modifiers: Modifiers,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let alt_key = modifiers.contains(Modifiers::ALT);
|
let alt_key = modifiers.contains(Modifiers::ALT);
|
||||||
@@ -103,6 +105,23 @@ impl PointData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for PointData {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("PointInteraction")
|
||||||
|
.field("coordinates", &self.coordinates())
|
||||||
|
.field("modifiers", &self.modifiers())
|
||||||
|
.field("held_buttons", &self.held_buttons())
|
||||||
|
.field("trigger_button", &self.trigger_button())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointInteraction for PointData {
|
||||||
|
fn get_point_data(&self) -> PointData {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait PointInteraction {
|
pub trait PointInteraction {
|
||||||
fn get_point_data(&self) -> PointData;
|
fn get_point_data(&self) -> PointData;
|
||||||
|
|
||||||
@@ -160,7 +179,7 @@ pub trait PointInteraction {
|
|||||||
decode_mouse_button_set(self.get_point_data().buttons)
|
decode_mouse_button_set(self.get_point_data().buttons)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trigger_button(&self) -> MouseButton {
|
fn trigger_button(&self) -> Option<MouseButton> {
|
||||||
MouseButton::from_web_code(self.get_point_data().button)
|
Some(MouseButton::from_web_code(self.get_point_data().button))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ use crate::events::{
|
|||||||
};
|
};
|
||||||
use crate::input_data::decode_key_location;
|
use crate::input_data::decode_key_location;
|
||||||
use crate::point_interaction::PointData;
|
use crate::point_interaction::PointData;
|
||||||
use crate::{DragData, MountedData};
|
use crate::{DragData, MountedData, PointerEventData};
|
||||||
use keyboard_types::{Code, Key, Modifiers};
|
use keyboard_types::{Code, Key, Modifiers};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@@ -147,16 +147,18 @@ impl From<&PointerEvent> for PointerData {
|
|||||||
page_x: e.page_x(),
|
page_x: e.page_x(),
|
||||||
page_y: e.page_y(),
|
page_y: e.page_y(),
|
||||||
},
|
},
|
||||||
e.pointer_id(),
|
PointerEventData {
|
||||||
e.width(),
|
pointer_id: e.pointer_id(),
|
||||||
e.height(),
|
width: e.width(),
|
||||||
e.pressure(),
|
height: e.height(),
|
||||||
e.tangential_pressure(),
|
pressure: e.pressure(),
|
||||||
e.tilt_x(),
|
tangential_pressure: e.tangential_pressure(),
|
||||||
e.tilt_y(),
|
tilt_x: e.tilt_x(),
|
||||||
e.twist(),
|
tilt_y: e.tilt_y(),
|
||||||
e.pointer_type(),
|
twist: e.twist(),
|
||||||
e.is_primary(),
|
pointer_type: e.pointer_type(),
|
||||||
|
is_primary: e.is_primary(),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-10
@@ -13,6 +13,7 @@ use dioxus_html::geometry::{
|
|||||||
use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
|
use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
|
||||||
use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
|
use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons;
|
||||||
use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
|
use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet};
|
||||||
|
use dioxus_html::point_interaction::{PointData, PointInteraction};
|
||||||
use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData};
|
use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -133,12 +134,12 @@ impl InnerInputState {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_mouse_data = MouseData::new(
|
let new_mouse_data = MouseData::new(PointData::new(
|
||||||
m.coordinates(),
|
|
||||||
m.trigger_button(),
|
m.trigger_button(),
|
||||||
held_buttons,
|
held_buttons,
|
||||||
|
m.coordinates(),
|
||||||
m.modifiers(),
|
m.modifiers(),
|
||||||
);
|
));
|
||||||
|
|
||||||
self.mouse = Some(new_mouse_data.clone());
|
self.mouse = Some(new_mouse_data.clone());
|
||||||
*m = new_mouse_data;
|
*m = new_mouse_data;
|
||||||
@@ -291,12 +292,12 @@ impl InnerInputState {
|
|||||||
mouse_data.page_coordinates(),
|
mouse_data.page_coordinates(),
|
||||||
);
|
);
|
||||||
|
|
||||||
MouseData::new(
|
MouseData::new(PointData::new(
|
||||||
coordinates,
|
|
||||||
mouse_data.trigger_button(),
|
mouse_data.trigger_button(),
|
||||||
mouse_data.held_buttons(),
|
mouse_data.held_buttons(),
|
||||||
|
coordinates,
|
||||||
mouse_data.modifiers(),
|
mouse_data.modifiers(),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mouse_data) = &self.mouse {
|
if let Some(mouse_data) = &self.mouse {
|
||||||
@@ -715,12 +716,12 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// held mouse buttons get set later by maintaining state, as crossterm does not provide them
|
// held mouse buttons get set later by maintaining state, as crossterm does not provide them
|
||||||
EventData::Mouse(MouseData::new(
|
EventData::Mouse(MouseData::new(PointData::new(
|
||||||
coordinates,
|
|
||||||
button,
|
button,
|
||||||
DioxusMouseButtons::empty(),
|
MouseButtonSet::empty(),
|
||||||
|
coordinates,
|
||||||
modifiers,
|
modifiers,
|
||||||
))
|
)))
|
||||||
};
|
};
|
||||||
|
|
||||||
let get_wheel_data = |up| {
|
let get_wheel_data = |up| {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData};
|
use dioxus_html::{
|
||||||
|
input_data::keyboard_types::Key, point_interaction::PointInteraction, KeyboardData, MouseData,
|
||||||
|
};
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::{
|
||||||
custom_element::CustomElement,
|
custom_element::CustomElement,
|
||||||
node::{OwnedAttributeDiscription, OwnedAttributeValue},
|
node::{OwnedAttributeDiscription, OwnedAttributeValue},
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use std::{collections::HashMap, io::stdout};
|
use std::{collections::HashMap, io::stdout};
|
||||||
|
|
||||||
use crossterm::{cursor::MoveTo, execute};
|
use crossterm::{cursor::MoveTo, execute};
|
||||||
use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData};
|
use dioxus_html::{
|
||||||
|
input_data::keyboard_types::Key, point_interaction::PointInteraction, KeyboardData, MouseData,
|
||||||
|
};
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::{
|
||||||
custom_element::CustomElement,
|
custom_element::CustomElement,
|
||||||
node::OwnedAttributeDiscription,
|
node::OwnedAttributeDiscription,
|
||||||
|
|||||||
Reference in new issue
Block a user