mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-08-03 09:10:13 +08:00
opt paint text selection rect
add patch desc Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
@@ -295,7 +295,7 @@ class RenderEditable extends RenderBox
|
|||||||
Offset cursorOffset = Offset.zero,
|
Offset cursorOffset = Offset.zero,
|
||||||
double devicePixelRatio = 1.0,
|
double devicePixelRatio = 1.0,
|
||||||
ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.max,
|
ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.max,
|
||||||
ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.max,
|
ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight,
|
||||||
bool? enableInteractiveSelection,
|
bool? enableInteractiveSelection,
|
||||||
this.floatingCursorAddedMargin = const EdgeInsets.fromLTRB(4, 4, 4, 5),
|
this.floatingCursorAddedMargin = const EdgeInsets.fromLTRB(4, 4, 4, 5),
|
||||||
TextRange? promptRectRange,
|
TextRange? promptRectRange,
|
||||||
@@ -3154,17 +3154,44 @@ class _TextHighlightPainter extends RenderEditablePainter {
|
|||||||
|
|
||||||
highlightPaint.color = color;
|
highlightPaint.color = color;
|
||||||
final TextPainter textPainter = renderEditable._textPainter;
|
final TextPainter textPainter = renderEditable._textPainter;
|
||||||
final Set<TextBox> boxes = textPainter
|
|
||||||
.getBoxesForSelection(
|
|
||||||
TextSelection(baseOffset: range.start, extentOffset: range.end),
|
|
||||||
boxHeightStyle: selectionHeightStyle,
|
|
||||||
boxWidthStyle: selectionWidthStyle,
|
|
||||||
)
|
|
||||||
.toSet();
|
|
||||||
|
|
||||||
for (final box in boxes) {
|
final List<ui.TextBox> boxes = textPainter.getBoxesForSelection(
|
||||||
|
TextSelection(baseOffset: range.start, extentOffset: range.end),
|
||||||
|
boxHeightStyle: ui.BoxHeightStyle.max,
|
||||||
|
boxWidthStyle: ui.BoxWidthStyle.tight,
|
||||||
|
);
|
||||||
|
ui.TextBox? mergedBox;
|
||||||
|
for (final textBox in boxes) {
|
||||||
|
if (mergedBox == null) {
|
||||||
|
mergedBox = textBox;
|
||||||
|
} else if (mergedBox.top != textBox.top) {
|
||||||
|
canvas.drawRect(
|
||||||
|
Rect.fromLTRB(
|
||||||
|
mergedBox.left,
|
||||||
|
mergedBox.top,
|
||||||
|
size.width,
|
||||||
|
mergedBox.bottom,
|
||||||
|
)
|
||||||
|
.shift(renderEditable._paintOffset)
|
||||||
|
.intersect(
|
||||||
|
Rect.fromLTRB(0, 0, textPainter.width, textPainter.height),
|
||||||
|
),
|
||||||
|
highlightPaint,
|
||||||
|
);
|
||||||
|
mergedBox = textBox;
|
||||||
|
} else {
|
||||||
|
mergedBox = TextBox.fromLTRBD(
|
||||||
|
mergedBox.left,
|
||||||
|
mergedBox.top,
|
||||||
|
textBox.right,
|
||||||
|
mergedBox.bottom,
|
||||||
|
mergedBox.direction,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mergedBox != null) {
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
box
|
mergedBox
|
||||||
.toRect()
|
.toRect()
|
||||||
.shift(renderEditable._paintOffset)
|
.shift(renderEditable._paintOffset)
|
||||||
.intersect(
|
.intersect(
|
||||||
|
|||||||
@@ -1717,7 +1717,7 @@ class EditableText extends StatefulWidget {
|
|||||||
// }
|
// }
|
||||||
// return ui.BoxWidthStyle.tight;
|
// return ui.BoxWidthStyle.tight;
|
||||||
// }
|
// }
|
||||||
return ui.BoxWidthStyle.max;
|
return ui.BoxWidthStyle.tight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The default value for [stylusHandwritingEnabled].
|
/// The default value for [stylusHandwritingEnabled].
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class _DynamicDetailPageState
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic _scrollable;
|
ScrollableState? _scrollable;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -136,6 +136,7 @@ class _DynamicDetailPageState
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
return SelectionTapRegionSurface(
|
return SelectionTapRegionSurface(
|
||||||
|
/// apply `lib/scripts/scrollable.patch`
|
||||||
isScrolling: () => _scrollable?.shouldIgnorePointer ?? false,
|
isScrolling: () => _scrollable?.shouldIgnorePointer ?? false,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,70 @@
|
|||||||
|
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart
|
||||||
|
index 5d0121e3f37..a42c758b2fb 100644
|
||||||
|
--- a/packages/flutter/lib/src/rendering/editable.dart
|
||||||
|
+++ b/packages/flutter/lib/src/rendering/editable.dart
|
||||||
|
@@ -321,7 +321,7 @@ class RenderEditable extends RenderBox
|
||||||
|
Offset cursorOffset = Offset.zero,
|
||||||
|
double devicePixelRatio = 1.0,
|
||||||
|
ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.max,
|
||||||
|
- ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.max,
|
||||||
|
+ ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight,
|
||||||
|
bool? enableInteractiveSelection,
|
||||||
|
this.floatingCursorAddedMargin = const EdgeInsets.fromLTRB(4, 4, 4, 5),
|
||||||
|
TextRange? promptRectRange,
|
||||||
|
@@ -2912,17 +2912,31 @@ class _TextHighlightPainter extends RenderEditablePainter {
|
||||||
|
|
||||||
|
highlightPaint.color = color;
|
||||||
|
final TextPainter textPainter = renderEditable._textPainter;
|
||||||
|
- final Set<TextBox> boxes = textPainter
|
||||||
|
- .getBoxesForSelection(
|
||||||
|
- TextSelection(baseOffset: range.start, extentOffset: range.end),
|
||||||
|
- boxHeightStyle: selectionHeightStyle,
|
||||||
|
- boxWidthStyle: selectionWidthStyle,
|
||||||
|
- )
|
||||||
|
- .toSet();
|
||||||
|
|
||||||
|
- for (final box in boxes) {
|
||||||
|
+ final List<ui.TextBox> boxes = textPainter.getBoxesForSelection(
|
||||||
|
+ TextSelection(baseOffset: range.start, extentOffset: range.end),
|
||||||
|
+ boxHeightStyle: ui.BoxHeightStyle.max,
|
||||||
|
+ boxWidthStyle: ui.BoxWidthStyle.tight,
|
||||||
|
+ );
|
||||||
|
+ ui.TextBox? mergedBox;
|
||||||
|
+ for (final textBox in boxes) {
|
||||||
|
+ if (mergedBox == null) {
|
||||||
|
+ mergedBox = textBox;
|
||||||
|
+ } else if (mergedBox.top != textBox.top) {
|
||||||
|
+ canvas.drawRect(
|
||||||
|
+ Rect.fromLTRB(mergedBox.left, mergedBox.top, size.width, mergedBox.bottom)
|
||||||
|
+ .shift(renderEditable._paintOffset)
|
||||||
|
+ .intersect(Rect.fromLTRB(0, 0, textPainter.width, textPainter.height)),
|
||||||
|
+ highlightPaint,
|
||||||
|
+ );
|
||||||
|
+ mergedBox = textBox;
|
||||||
|
+ } else {
|
||||||
|
+ mergedBox = TextBox.fromLTRBD(mergedBox.left, mergedBox.top, textBox.right, mergedBox.bottom, mergedBox.direction);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (mergedBox != null) {
|
||||||
|
canvas.drawRect(
|
||||||
|
- box
|
||||||
|
+ mergedBox
|
||||||
|
.toRect()
|
||||||
|
.shift(renderEditable._paintOffset)
|
||||||
|
.intersect(Rect.fromLTWH(0, 0, textPainter.width, textPainter.height)),
|
||||||
diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
|
diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
|
||||||
index b1ae878f2cc..8a7540350ef 100644
|
index b1ae878f2cc..7b179c7e3b3 100644
|
||||||
--- a/packages/flutter/lib/src/widgets/editable_text.dart
|
--- a/packages/flutter/lib/src/widgets/editable_text.dart
|
||||||
+++ b/packages/flutter/lib/src/widgets/editable_text.dart
|
+++ b/packages/flutter/lib/src/widgets/editable_text.dart
|
||||||
|
@@ -2099,7 +2099,7 @@ class EditableText extends StatefulWidget {
|
||||||
|
}
|
||||||
|
return ui.BoxWidthStyle.tight;
|
||||||
|
}
|
||||||
|
- return ui.BoxWidthStyle.max;
|
||||||
|
+ return ui.BoxWidthStyle.tight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The default value for [stylusHandwritingEnabled].
|
||||||
@@ -5046,7 +5046,9 @@ class EditableTextState extends State<EditableText>
|
@@ -5046,7 +5046,9 @@ class EditableTextState extends State<EditableText>
|
||||||
void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause? cause) {
|
void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause? cause) {
|
||||||
// Compare the current TextEditingValue with the pre-format new
|
// Compare the current TextEditingValue with the pre-format new
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ param(
|
|||||||
# https://github.com/flutter/flutter/issues/182281
|
# https://github.com/flutter/flutter/issues/182281
|
||||||
$NewOverScrollIndicator = "362b1de29974ffc1ed6faa826e1df870d7bec75f";
|
$NewOverScrollIndicator = "362b1de29974ffc1ed6faa826e1df870d7bec75f";
|
||||||
|
|
||||||
|
# set `gestureSettings`
|
||||||
$BottomSheetAndroidPatch = "lib/scripts/bottom_sheet_android.patch"
|
$BottomSheetAndroidPatch = "lib/scripts/bottom_sheet_android.patch"
|
||||||
|
|
||||||
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1906
|
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1906
|
||||||
@@ -17,9 +18,11 @@ $BottomSheetIOSPiliPlusPatch = "lib/scripts/bottom_sheet_ios_piliplus.patch"
|
|||||||
$TextSelectionMenuFix = "beb2ad17004a1b118ff2bd09f55cee23198f6652";
|
$TextSelectionMenuFix = "beb2ad17004a1b118ff2bd09f55cee23198f6652";
|
||||||
|
|
||||||
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1662
|
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1662
|
||||||
|
# handle bottom scroll event
|
||||||
$ScrollViewPatch = "lib/scripts/scroll_view.patch"
|
$ScrollViewPatch = "lib/scripts/scroll_view.patch"
|
||||||
|
|
||||||
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2106
|
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2106
|
||||||
|
# use `TouchGestureRecognizer` on all platforms
|
||||||
$TextSelectionPatch = "lib/scripts/text_selection.patch"
|
$TextSelectionPatch = "lib/scripts/text_selection.patch"
|
||||||
|
|
||||||
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1947
|
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1947
|
||||||
@@ -28,28 +31,46 @@ $NavigatorPatch = "lib/scripts/navigator.patch"
|
|||||||
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2107
|
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2107
|
||||||
$ImageAnimPatch = "lib/scripts/image_anim.patch"
|
$ImageAnimPatch = "lib/scripts/image_anim.patch"
|
||||||
|
|
||||||
|
# remove `_scheduleRebuild`
|
||||||
$LayoutBuilderPatch = "lib/scripts/layout_builder.patch"
|
$LayoutBuilderPatch = "lib/scripts/layout_builder.patch"
|
||||||
|
|
||||||
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2308
|
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2308
|
||||||
$NavigationDrawerPatch = "lib/scripts/navigation_drawer.patch"
|
$NavigationDrawerPatch = "lib/scripts/navigation_drawer.patch"
|
||||||
|
|
||||||
|
# apply text color to icon color
|
||||||
$PopupMenuPatch = "lib/scripts/popup_menu.patch"
|
$PopupMenuPatch = "lib/scripts/popup_menu.patch"
|
||||||
|
|
||||||
|
# remove `Hero` effect
|
||||||
$FABPatch = "lib/scripts/fab.patch"
|
$FABPatch = "lib/scripts/fab.patch"
|
||||||
|
|
||||||
$SelectableRegionSelectionPatch = "lib/scripts/selectable_region.patch"
|
# https://github.com/flutter/flutter/issues/139890
|
||||||
|
# https://github.com/flutter/flutter/issues/174689
|
||||||
|
# separator support
|
||||||
|
# clamp handle offset
|
||||||
|
# widgetspan selection support
|
||||||
|
# clear selection when tapping outside
|
||||||
|
# free selection if there is only one text
|
||||||
|
# clamp dragging selection behavior on Android
|
||||||
|
# show selection menu if secondary tap position is in text region on desktop
|
||||||
|
$SelectableRegionPatch = "lib/scripts/selectable_region.patch"
|
||||||
|
|
||||||
|
# https://github.com/flutter/flutter/issues/132047
|
||||||
|
# https://github.com/flutter/flutter/issues/174689
|
||||||
$EditableTextPatch = "lib/scripts/editable_text.patch"
|
$EditableTextPatch = "lib/scripts/editable_text.patch"
|
||||||
|
|
||||||
|
# set `selectAllOnFocus` to `false` by default
|
||||||
$TextFieldPatch = "lib/scripts/text_field.patch"
|
$TextFieldPatch = "lib/scripts/text_field.patch"
|
||||||
|
|
||||||
|
# notify `userScrollDirection` only if position is actually changing
|
||||||
$ScrollPositionPatch = "lib/scripts/scroll_position.patch"
|
$ScrollPositionPatch = "lib/scripts/scroll_position.patch"
|
||||||
|
|
||||||
|
# expose `_shouldIgnorePointer`
|
||||||
$ScrollablePatch = "lib/scripts/scrollable.patch"
|
$ScrollablePatch = "lib/scripts/scrollable.patch"
|
||||||
|
|
||||||
# TODO: remove
|
# TODO: remove
|
||||||
|
# https://github.com/flutter/flutter/issues/124078
|
||||||
# https://github.com/flutter/flutter/pull/183261
|
# https://github.com/flutter/flutter/pull/183261
|
||||||
$SelectableRegionPatch = "lib/scripts/null_safety_for_selectable_region.patch"
|
$NullSafetySelectableRegionPatch = "lib/scripts/null_safety_for_selectable_region.patch"
|
||||||
|
|
||||||
# TODO: remove
|
# TODO: remove
|
||||||
# https://github.com/flutter/flutter/issues/90223
|
# https://github.com/flutter/flutter/issues/90223
|
||||||
@@ -78,9 +99,9 @@ $picks = @($TextSelectionMenuFix)
|
|||||||
$reverts = @()
|
$reverts = @()
|
||||||
$patches = @($ModalBarrierPatch, $TextSelectionPatch, $MouseCursorPatch,
|
$patches = @($ModalBarrierPatch, $TextSelectionPatch, $MouseCursorPatch,
|
||||||
$ImageAnimPatch, $LayoutBuilderPatch, $NavigationDrawerPatch,
|
$ImageAnimPatch, $LayoutBuilderPatch, $NavigationDrawerPatch,
|
||||||
$PopupMenuPatch, $FABPatch, $SelectableRegionPatch, $SelectableRegionSelectionPatch,
|
$PopupMenuPatch, $FABPatch, $NullSafetySelectableRegionPatch,
|
||||||
$EditableTextPatch, $TextFieldPatch, $ScrollPositionPatch,
|
$SelectableRegionPatch, $EditableTextPatch, $TextFieldPatch,
|
||||||
$ScrollablePatch)
|
$ScrollPositionPatch, $ScrollablePatch)
|
||||||
|
|
||||||
switch ($platform.ToLower()) {
|
switch ($platform.ToLower()) {
|
||||||
"android" {
|
"android" {
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ index 09fba7fc4fb..57ee469358e 100644
|
|||||||
void computeSemanticsInformation(
|
void computeSemanticsInformation(
|
||||||
List<InlineSpanSemanticsInformation> collector, {
|
List<InlineSpanSemanticsInformation> collector, {
|
||||||
diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart
|
diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart
|
||||||
index 97a41dafb8d..15e7ce055e9 100644
|
index 97a41dafb8d..1dc2f36538d 100644
|
||||||
--- a/packages/flutter/lib/src/rendering/paragraph.dart
|
--- a/packages/flutter/lib/src/rendering/paragraph.dart
|
||||||
+++ b/packages/flutter/lib/src/rendering/paragraph.dart
|
+++ b/packages/flutter/lib/src/rendering/paragraph.dart
|
||||||
@@ -7,6 +7,7 @@
|
@@ -7,6 +7,7 @@
|
||||||
@@ -341,18 +341,34 @@ index 97a41dafb8d..15e7ce055e9 100644
|
|||||||
_setSelectionPosition(position, isEnd: isEnd);
|
_setSelectionPosition(position, isEnd: isEnd);
|
||||||
if (position.offset == range.end) {
|
if (position.offset == range.end) {
|
||||||
return SelectionResult.next;
|
return SelectionResult.next;
|
||||||
@@ -3568,7 +3626,10 @@ class _SelectableFragment
|
@@ -3568,8 +3626,25 @@ class _SelectableFragment
|
||||||
final selectionPaint = Paint()
|
final selectionPaint = Paint()
|
||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
..color = paragraph.selectionColor!;
|
..color = paragraph.selectionColor!;
|
||||||
- for (final TextBox textBox in paragraph.getBoxesForSelection(selection)) {
|
- for (final TextBox textBox in paragraph.getBoxesForSelection(selection)) {
|
||||||
+ for (final TextBox textBox in paragraph.getBoxesForSelection(
|
- context.canvas.drawRect(textBox.toRect().shift(offset), selectionPaint);
|
||||||
|
+
|
||||||
|
+ final List<ui.TextBox> boxes = paragraph.getBoxesForSelection(
|
||||||
+ selection,
|
+ selection,
|
||||||
+ boxHeightStyle: ui.BoxHeightStyle.max,
|
+ boxHeightStyle: ui.BoxHeightStyle.max,
|
||||||
+ )) {
|
+ boxWidthStyle: ui.BoxWidthStyle.tight,
|
||||||
context.canvas.drawRect(textBox.toRect().shift(offset), selectionPaint);
|
+ );
|
||||||
|
+ ui.TextBox? mergedBox;
|
||||||
|
+ for (final textBox in boxes) {
|
||||||
|
+ if (mergedBox == null) {
|
||||||
|
+ mergedBox = textBox;
|
||||||
|
+ } else if (mergedBox.top != textBox.top) {
|
||||||
|
+ context.canvas.drawRect(Rect.fromLTRB(mergedBox.left, mergedBox.top, paragraph.size.width, mergedBox.bottom).shift(offset), selectionPaint);
|
||||||
|
+ mergedBox = textBox;
|
||||||
|
+ } else {
|
||||||
|
+ mergedBox = TextBox.fromLTRBD(mergedBox.left, mergedBox.top, textBox.right, mergedBox.bottom, mergedBox.direction);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (mergedBox != null) {
|
||||||
|
+ context.canvas.drawRect(mergedBox.toRect().shift(offset), selectionPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_startHandleLayerLink != null && value.startSelectionPoint != null) {
|
||||||
diff --git a/packages/flutter/lib/src/rendering/selection.dart b/packages/flutter/lib/src/rendering/selection.dart
|
diff --git a/packages/flutter/lib/src/rendering/selection.dart b/packages/flutter/lib/src/rendering/selection.dart
|
||||||
index a813e141dc4..d045236d031 100644
|
index a813e141dc4..d045236d031 100644
|
||||||
--- a/packages/flutter/lib/src/rendering/selection.dart
|
--- a/packages/flutter/lib/src/rendering/selection.dart
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
|
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
|
||||||
import 'package:PiliPlus/utils/page_utils.dart';
|
import 'package:PiliPlus/utils/page_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart' show Selectable;
|
|
||||||
|
|
||||||
extension SelectableRegionStateExt on SelectableRegionState {
|
extension SelectableRegionStateExt on SelectableRegionState {
|
||||||
void addLaunchMenuIfNeeded(
|
void addLaunchMenuIfNeeded(
|
||||||
@@ -24,15 +23,11 @@ extension SelectableRegionStateExt on SelectableRegionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? get selectedText => ((this as dynamic).selectable as Selectable?)
|
/// apply `lib/scripts/selectable_region.patch`
|
||||||
?.getSelectedContent()
|
String? get selectedText => selectable?.getSelectedContent()?.plainText;
|
||||||
?.plainText;
|
|
||||||
|
|
||||||
bool get isUncollapsed =>
|
/// apply `lib/scripts/selectable_region.patch`
|
||||||
((this as dynamic).selectionDelegate as StaticSelectionContainerDelegate)
|
bool get isUncollapsed => selectionDelegate.value.status == .uncollapsed;
|
||||||
.value
|
|
||||||
.status ==
|
|
||||||
.uncollapsed;
|
|
||||||
|
|
||||||
void onMenuPressed(
|
void onMenuPressed(
|
||||||
ValueChanged<String> callback, {
|
ValueChanged<String> callback, {
|
||||||
|
|||||||
Reference in New Issue
Block a user