Refactored the HorizontallyScrollableText struct to handle Unicode strings more uniformly and to not rely on byte boundaries but instead to rely on Unicode char boundaries
This commit is contained in:
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "managarr"
|
name = "managarr"
|
||||||
version = "0.0.22"
|
version = "0.0.26"
|
||||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||||
description = "A TUI for managing *arr servers"
|
description = "A TUI for managing *arr servers"
|
||||||
keywords = ["managarr", "tui-rs", "dashboard", "servarr"]
|
keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui", "terminal"]
|
||||||
documentation = "https://github.com/Dark-Alex-17/managarr"
|
documentation = "https://github.com/Dark-Alex-17/managarr"
|
||||||
repository = "https://github.com/Dark-Alex-17/managarr"
|
repository = "https://github.com/Dark-Alex-17/managarr"
|
||||||
homepage = "https://github.com/Dark-Alex-17/managarr"
|
homepage = "https://github.com/Dark-Alex-17/managarr"
|
||||||
|
|||||||
+39
-4
@@ -270,14 +270,49 @@ impl HorizontallyScrollableText {
|
|||||||
|
|
||||||
pub fn pop(&mut self) {
|
pub fn pop(&mut self) {
|
||||||
if *self.offset.borrow() < self.len() {
|
if *self.offset.borrow() < self.len() {
|
||||||
self.text.remove(self.len() - *self.offset.borrow() - 1);
|
let (index, _) = self
|
||||||
|
.text
|
||||||
|
.chars()
|
||||||
|
.enumerate()
|
||||||
|
.nth(self.len() - *self.offset.borrow() - 1)
|
||||||
|
.unwrap();
|
||||||
|
self.text = self
|
||||||
|
.text
|
||||||
|
.chars()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(idx, _)| *idx != index)
|
||||||
|
.map(|tuple| tuple.1)
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, character: char) {
|
pub fn push(&mut self, character: char) {
|
||||||
self
|
if self.text.is_empty() {
|
||||||
.text
|
self.text.push(character);
|
||||||
.insert(self.len() - *self.offset.borrow(), character);
|
} else {
|
||||||
|
let index = self.len() - *self.offset.borrow();
|
||||||
|
|
||||||
|
if index == self.len() {
|
||||||
|
self.text.push(character);
|
||||||
|
} else {
|
||||||
|
let mut new_text = String::new();
|
||||||
|
self
|
||||||
|
.text
|
||||||
|
.chars()
|
||||||
|
.collect::<Vec<char>>()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(idx, &c)| {
|
||||||
|
if idx == index {
|
||||||
|
new_text.push(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_text.push(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.text = new_text;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+87
-14
@@ -340,10 +340,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_text_len() {
|
fn test_horizontally_scrollable_text_len() {
|
||||||
let test_text = "우리 생애 최고의 해 The.Best";
|
let test_text = "우리 생애 최고의 해Test.Text";
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText::new(test_text.to_owned());
|
let horizontally_scrollable_text = HorizontallyScrollableText::new(test_text.to_owned());
|
||||||
|
|
||||||
assert_eq!(horizontally_scrollable_text.len(), 20);
|
assert_eq!(horizontally_scrollable_text.len(), 20);
|
||||||
|
assert_eq!(horizontally_scrollable_text.text.len(), 36);
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,6 +368,26 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_horizontally_scrollable_text_scroll_text_left_uses_len_method() {
|
||||||
|
let horizontally_scrollable_text = HorizontallyScrollableText::from("우리");
|
||||||
|
|
||||||
|
horizontally_scrollable_text.scroll_left();
|
||||||
|
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
||||||
|
assert_str_eq!(horizontally_scrollable_text.to_string(), "리");
|
||||||
|
|
||||||
|
horizontally_scrollable_text.scroll_left();
|
||||||
|
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
|
||||||
|
assert_str_eq!(horizontally_scrollable_text.to_string(), "");
|
||||||
|
|
||||||
|
horizontally_scrollable_text.scroll_left();
|
||||||
|
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
|
||||||
|
assert!(horizontally_scrollable_text.to_string().is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_text_scroll_text_right() {
|
fn test_horizontally_scrollable_text_scroll_text_right() {
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText::from("Test string");
|
let horizontally_scrollable_text = HorizontallyScrollableText::from("Test string");
|
||||||
@@ -398,6 +419,15 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_horizontally_scrollable_text_scroll_home_uses_len_method() {
|
||||||
|
let horizontally_scrollable_text = HorizontallyScrollableText::from("우리");
|
||||||
|
|
||||||
|
horizontally_scrollable_text.scroll_home();
|
||||||
|
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_text_reset_offset() {
|
fn test_horizontally_scrollable_text_reset_offset() {
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText {
|
let horizontally_scrollable_text = HorizontallyScrollableText {
|
||||||
@@ -411,7 +441,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_text_scroll_or_reset() {
|
fn test_horizontally_scrollable_text_scroll_left_or_reset() {
|
||||||
let width = 3;
|
let width = 3;
|
||||||
let test_text = "Test string";
|
let test_text = "Test string";
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText::from(test_text);
|
let horizontally_scrollable_text = HorizontallyScrollableText::from(test_text);
|
||||||
@@ -438,7 +468,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_test_scroll_or_reset_resets_when_text_unselected() {
|
fn test_horizontally_scrollable_text_scroll_left_or_reset_resets_when_text_unselected() {
|
||||||
let horizontally_scrollable_test = HorizontallyScrollableText::from("Test string");
|
let horizontally_scrollable_test = HorizontallyScrollableText::from("Test string");
|
||||||
horizontally_scrollable_test.scroll_left();
|
horizontally_scrollable_test.scroll_left();
|
||||||
|
|
||||||
@@ -449,55 +479,98 @@ mod tests {
|
|||||||
assert_eq!(*horizontally_scrollable_test.offset.borrow(), 0);
|
assert_eq!(*horizontally_scrollable_test.offset.borrow(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_horizontally_scrollable_text_scroll_left_or_reset_uses_len_method() {
|
||||||
|
let horizontally_scrollable_text = HorizontallyScrollableText::from("우리");
|
||||||
|
let width = 1;
|
||||||
|
|
||||||
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
||||||
|
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
||||||
|
|
||||||
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
||||||
|
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
|
||||||
|
|
||||||
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
||||||
|
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_text_pop() {
|
fn test_horizontally_scrollable_text_pop() {
|
||||||
let test_text = "Test string";
|
let test_text = "Test sTrin우gs";
|
||||||
let mut horizontally_scrollable_text = HorizontallyScrollableText::from(test_text);
|
let mut horizontally_scrollable_text = HorizontallyScrollableText::from(test_text);
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test strin");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin우g");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test strn");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test sTring");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test str");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_home();
|
horizontally_scrollable_text.scroll_home();
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test str");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 8);
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 10);
|
||||||
|
|
||||||
|
horizontally_scrollable_text.scroll_right();
|
||||||
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
|
assert_str_eq!(horizontally_scrollable_text.text, "est sTrin");
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_horizontally_scrollable_text_pop_uses_len_method() {
|
||||||
|
let mut horizontally_scrollable_text = HorizontallyScrollableText::from("우리");
|
||||||
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
|
assert_str_eq!(horizontally_scrollable_text.text, "우");
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
||||||
|
|
||||||
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
|
assert!(horizontally_scrollable_text.text.is_empty());
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
||||||
|
|
||||||
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
|
assert!(horizontally_scrollable_text.text.is_empty());
|
||||||
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_text_push() {
|
fn test_horizontally_scrollable_text_push() {
|
||||||
let test_text = "Test string";
|
let test_text = "Test stri우ng";
|
||||||
let mut horizontally_scrollable_text = HorizontallyScrollableText::from(test_text);
|
let mut horizontally_scrollable_text = HorizontallyScrollableText::from(test_text);
|
||||||
horizontally_scrollable_text.push('h');
|
horizontally_scrollable_text.push('h');
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test stringh");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우ngh");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
horizontally_scrollable_text.push('l');
|
horizontally_scrollable_text.push('l');
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test stringlh");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우nglh");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.push('0');
|
horizontally_scrollable_text.push('리');
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test stringlh0");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우nglh리");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ impl DrawUi for CollectionsUi {
|
|||||||
draw_collections,
|
draw_collections,
|
||||||
draw_search_box,
|
draw_search_box,
|
||||||
30,
|
30,
|
||||||
11,
|
13,
|
||||||
),
|
),
|
||||||
ActiveRadarrBlock::FilterCollections => draw_popup_over(
|
ActiveRadarrBlock::FilterCollections => draw_popup_over(
|
||||||
f,
|
f,
|
||||||
@@ -56,7 +56,7 @@ impl DrawUi for CollectionsUi {
|
|||||||
draw_collections,
|
draw_collections,
|
||||||
draw_filter_box,
|
draw_filter_box,
|
||||||
30,
|
30,
|
||||||
11,
|
13,
|
||||||
),
|
),
|
||||||
ActiveRadarrBlock::UpdateAllCollectionsPrompt => draw_prompt_popup_over(
|
ActiveRadarrBlock::UpdateAllCollectionsPrompt => draw_prompt_popup_over(
|
||||||
f,
|
f,
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ impl DrawUi for LibraryUi {
|
|||||||
{
|
{
|
||||||
ActiveRadarrBlock::Movies => draw_library(f, app, content_rect),
|
ActiveRadarrBlock::Movies => draw_library(f, app, content_rect),
|
||||||
ActiveRadarrBlock::SearchMovie => {
|
ActiveRadarrBlock::SearchMovie => {
|
||||||
draw_popup_over(f, app, content_rect, draw_library, draw_search_box, 30, 11)
|
draw_popup_over(f, app, content_rect, draw_library, draw_search_box, 30, 13)
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::FilterMovies => {
|
ActiveRadarrBlock::FilterMovies => {
|
||||||
draw_popup_over(f, app, content_rect, draw_library, draw_filter_box, 30, 11)
|
draw_popup_over(f, app, content_rect, draw_library, draw_filter_box, 30, 13)
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::UpdateAllMoviesPrompt => draw_prompt_popup_over(
|
ActiveRadarrBlock::UpdateAllMoviesPrompt => draw_prompt_popup_over(
|
||||||
f,
|
f,
|
||||||
|
|||||||
+31
-6
@@ -25,8 +25,9 @@ use crate::ui::radarr_ui::root_folders::RootFoldersUi;
|
|||||||
use crate::ui::radarr_ui::system::SystemUi;
|
use crate::ui::radarr_ui::system::SystemUi;
|
||||||
use crate::ui::utils::{
|
use crate::ui::utils::{
|
||||||
borderless_block, horizontal_chunks, layout_block, line_gauge_with_label, line_gauge_with_title,
|
borderless_block, horizontal_chunks, layout_block, line_gauge_with_label, line_gauge_with_title,
|
||||||
show_cursor, style_awaiting_import, style_bold, style_default, style_failure, style_success,
|
show_cursor, style_awaiting_import, style_bold, style_default, style_failure, style_help,
|
||||||
style_unmonitored, style_warning, title_block, title_block_centered, vertical_chunks_with_margin,
|
style_success, style_unmonitored, style_warning, title_block, title_block_centered,
|
||||||
|
vertical_chunks_with_margin,
|
||||||
};
|
};
|
||||||
use crate::ui::DrawUi;
|
use crate::ui::DrawUi;
|
||||||
use crate::utils::convert_to_gb;
|
use crate::utils::convert_to_gb;
|
||||||
@@ -231,8 +232,15 @@ fn determine_row_style(downloads_vec: &[DownloadRecord], movie: &Movie) -> Style
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_search_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) {
|
fn draw_search_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) {
|
||||||
let chunks =
|
let chunks = vertical_chunks_with_margin(
|
||||||
vertical_chunks_with_margin(vec![Constraint::Length(3), Constraint::Min(0)], area, 1);
|
vec![
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(1),
|
||||||
|
Constraint::Min(0),
|
||||||
|
],
|
||||||
|
area,
|
||||||
|
1,
|
||||||
|
);
|
||||||
if !app.data.radarr_data.is_searching {
|
if !app.data.radarr_data.is_searching {
|
||||||
let error_msg = match app.get_current_route() {
|
let error_msg = match app.get_current_route() {
|
||||||
Route::Radarr(active_radarr_block, _) => match active_radarr_block {
|
Route::Radarr(active_radarr_block, _) => match active_radarr_block {
|
||||||
@@ -272,15 +280,27 @@ fn draw_search_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Re
|
|||||||
let input = Paragraph::new(block_content.as_str())
|
let input = Paragraph::new(block_content.as_str())
|
||||||
.style(style_default())
|
.style(style_default())
|
||||||
.block(title_block_centered(block_title));
|
.block(title_block_centered(block_title));
|
||||||
|
let help = Paragraph::new("<esc> cancel")
|
||||||
|
.style(style_help())
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.block(borderless_block());
|
||||||
show_cursor(f, chunks[0], offset, block_content);
|
show_cursor(f, chunks[0], offset, block_content);
|
||||||
|
|
||||||
f.render_widget(input, chunks[0]);
|
f.render_widget(input, chunks[0]);
|
||||||
|
f.render_widget(help, chunks[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_filter_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) {
|
fn draw_filter_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) {
|
||||||
let chunks =
|
let chunks = vertical_chunks_with_margin(
|
||||||
vertical_chunks_with_margin(vec![Constraint::Length(3), Constraint::Min(0)], area, 1);
|
vec![
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(1),
|
||||||
|
Constraint::Min(0),
|
||||||
|
],
|
||||||
|
area,
|
||||||
|
1,
|
||||||
|
);
|
||||||
if !app.data.radarr_data.is_filtering {
|
if !app.data.radarr_data.is_filtering {
|
||||||
let error_msg = match app.get_current_route() {
|
let error_msg = match app.get_current_route() {
|
||||||
Route::Radarr(active_radarr_block, _) => match active_radarr_block {
|
Route::Radarr(active_radarr_block, _) => match active_radarr_block {
|
||||||
@@ -320,9 +340,14 @@ fn draw_filter_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Re
|
|||||||
let input = Paragraph::new(block_content.as_str())
|
let input = Paragraph::new(block_content.as_str())
|
||||||
.style(style_default())
|
.style(style_default())
|
||||||
.block(title_block_centered(block_title));
|
.block(title_block_centered(block_title));
|
||||||
|
let help = Paragraph::new("<esc> cancel")
|
||||||
|
.style(style_help())
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.block(borderless_block());
|
||||||
show_cursor(f, chunks[0], offset, block_content);
|
show_cursor(f, chunks[0], offset, block_content);
|
||||||
|
|
||||||
f.render_widget(input, chunks[0]);
|
f.render_widget(input, chunks[0]);
|
||||||
|
f.render_widget(help, chunks[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl DrawUi for RootFoldersUi {
|
|||||||
draw_root_folders,
|
draw_root_folders,
|
||||||
draw_add_root_folder_prompt_box,
|
draw_add_root_folder_prompt_box,
|
||||||
30,
|
30,
|
||||||
15,
|
13,
|
||||||
),
|
),
|
||||||
ActiveRadarrBlock::DeleteRootFolderPrompt => draw_prompt_popup_over(
|
ActiveRadarrBlock::DeleteRootFolderPrompt => draw_prompt_popup_over(
|
||||||
f,
|
f,
|
||||||
|
|||||||
Reference in New Issue
Block a user