mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-15 18:59:54 +00:00
Merge pull request #1091 from lkzhao/ASTextKitFontSizeAdjuster
[ASTextNode] Support iteratively shrinking font size in order to ensure no single words are wrapped.
This commit is contained in:
commit
508e3db8ee
@ -298,6 +298,11 @@
|
||||
9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; };
|
||||
A32FEDD51C501B6A004F642A /* ASTextKitFontSizeAdjuster.h in Headers */ = {isa = PBXBuildFile; fileRef = A32FEDD31C501B6A004F642A /* ASTextKitFontSizeAdjuster.h */; };
|
||||
A32FEDD61C501B6A004F642A /* ASTextKitFontSizeAdjuster.m in Sources */ = {isa = PBXBuildFile; fileRef = A32FEDD41C501B6A004F642A /* ASTextKitFontSizeAdjuster.m */; };
|
||||
A32FEDD71C5042C1004F642A /* ASTextKitFontSizeAdjuster.m in Sources */ = {isa = PBXBuildFile; fileRef = A32FEDD41C501B6A004F642A /* ASTextKitFontSizeAdjuster.m */; };
|
||||
A373200F1C571B730011FC94 /* ASTextNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = A373200E1C571B050011FC94 /* ASTextNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
A37320101C571B740011FC94 /* ASTextNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = A373200E1C571B050011FC94 /* ASTextNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AC026B581BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */; };
|
||||
AC026B691BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@ -718,6 +723,9 @@
|
||||
9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; sourceTree = "<group>"; };
|
||||
9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = "<group>"; };
|
||||
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = "<group>"; };
|
||||
A32FEDD31C501B6A004F642A /* ASTextKitFontSizeAdjuster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitFontSizeAdjuster.h; path = TextKit/ASTextKitFontSizeAdjuster.h; sourceTree = "<group>"; };
|
||||
A32FEDD41C501B6A004F642A /* ASTextKitFontSizeAdjuster.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASTextKitFontSizeAdjuster.m; path = TextKit/ASTextKitFontSizeAdjuster.m; sourceTree = "<group>"; };
|
||||
A373200E1C571B050011FC94 /* ASTextNode+Beta.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASTextNode+Beta.h"; sourceTree = "<group>"; };
|
||||
AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASStaticLayoutSpecSnapshotTests.m; sourceTree = "<group>"; };
|
||||
AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASChangeSetDataController.h; sourceTree = "<group>"; };
|
||||
AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASChangeSetDataController.m; sourceTree = "<group>"; };
|
||||
@ -956,6 +964,7 @@
|
||||
AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */,
|
||||
0574D5E119C110610097DC25 /* ASTableViewProtocols.h */,
|
||||
058D09DF195D050800B7D73C /* ASTextNode.h */,
|
||||
A373200E1C571B050011FC94 /* ASTextNode+Beta.h */,
|
||||
058D09E0195D050800B7D73C /* ASTextNode.mm */,
|
||||
ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */,
|
||||
ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */,
|
||||
@ -1187,6 +1196,8 @@
|
||||
257754A01BEE44CD00737CA5 /* ASTextKitShadower.mm */,
|
||||
257754A11BEE44CD00737CA5 /* ASTextKitTailTruncater.h */,
|
||||
257754A21BEE44CD00737CA5 /* ASTextKitTailTruncater.mm */,
|
||||
A32FEDD31C501B6A004F642A /* ASTextKitFontSizeAdjuster.h */,
|
||||
A32FEDD41C501B6A004F642A /* ASTextKitFontSizeAdjuster.m */,
|
||||
257754A31BEE44CD00737CA5 /* ASTextKitTruncating.h */,
|
||||
257754A41BEE44CD00737CA5 /* ASEqualityHashHelpers.h */,
|
||||
2577548F1BED289A00737CA5 /* ASEqualityHashHelpers.mm */,
|
||||
@ -1283,11 +1294,13 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
257754C21BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h in Headers */,
|
||||
A373200F1C571B730011FC94 /* ASTextNode+Beta.h in Headers */,
|
||||
92DD2FE31BF4B97E0074C9DD /* ASMapNode.h in Headers */,
|
||||
AC026B691BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */,
|
||||
058D0A71195D05F800B7D73C /* _AS-objc-internal.h in Headers */,
|
||||
058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */,
|
||||
058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */,
|
||||
A32FEDD51C501B6A004F642A /* ASTextKitFontSizeAdjuster.h in Headers */,
|
||||
058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */,
|
||||
058D0A6D195D05EC00B7D73C /* _ASAsyncTransactionGroup.h in Headers */,
|
||||
058D0A72195D05F800B7D73C /* _ASCoreAnimationExtras.h in Headers */,
|
||||
@ -1459,6 +1472,7 @@
|
||||
B35062171B010EFD0018CF92 /* ASDataController.h in Headers */,
|
||||
B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */,
|
||||
34EFC75B1B701BAF00AD841F /* ASDimension.h in Headers */,
|
||||
A37320101C571B740011FC94 /* ASTextNode+Beta.h in Headers */,
|
||||
254C6B801BF94DF4003EC431 /* ASEqualityHashHelpers.h in Headers */,
|
||||
B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */,
|
||||
B35061FD1B010EFD0018CF92 /* ASDisplayNode+Subclasses.h in Headers */,
|
||||
@ -1784,6 +1798,7 @@
|
||||
257754B01BEE44CD00737CA5 /* ASTextKitRenderer+TextChecking.mm in Sources */,
|
||||
0516FA411A1563D200B4EBED /* ASMultiplexImageNode.mm in Sources */,
|
||||
DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */,
|
||||
A32FEDD61C501B6A004F642A /* ASTextKitFontSizeAdjuster.m in Sources */,
|
||||
058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */,
|
||||
055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */,
|
||||
ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */,
|
||||
@ -1917,6 +1932,7 @@
|
||||
9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */,
|
||||
9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */,
|
||||
DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */,
|
||||
A32FEDD71C5042C1004F642A /* ASTextKitFontSizeAdjuster.m in Sources */,
|
||||
254C6B841BF94F8A003EC431 /* ASTextNodeWordKerner.m in Sources */,
|
||||
34EFC76B1B701CEB00AD841F /* ASLayoutSpec.mm in Sources */,
|
||||
254C6B8C1BF94F8A003EC431 /* ASTextKitTailTruncater.mm in Sources */,
|
||||
|
||||
18
AsyncDisplayKit/ASTextNode+Beta.h
Normal file
18
AsyncDisplayKit/ASTextNode+Beta.h
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// ASTextNode+Beta.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Luke on 1/25/16.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
@interface ASDisplayNode (Beta)
|
||||
|
||||
/**
|
||||
@abstract The minimum scale that the textnode can apply to fit long words.
|
||||
@default 0 (No scaling)
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat minimumScaleFactor;
|
||||
|
||||
@end
|
||||
@ -255,6 +255,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
.lineBreakMode = _truncationMode,
|
||||
.maximumNumberOfLines = _maximumNumberOfLines,
|
||||
.exclusionPaths = _exclusionPaths,
|
||||
.minimumScaleFactor = _minimumScaleFactor,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1068,6 +1069,17 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
return visibleRange.length < _attributedString.length;
|
||||
}
|
||||
|
||||
- (void)setMinimumScaleFactor:(CGFloat)minimumScaleFactor
|
||||
{
|
||||
if (_minimumScaleFactor != minimumScaleFactor) {
|
||||
_minimumScaleFactor = minimumScaleFactor;
|
||||
[self _invalidateRenderer];
|
||||
ASDisplayNodeRespectThreadAffinityOfNode(self, ^{
|
||||
[self setNeedsDisplay];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines
|
||||
{
|
||||
if (_maximumNumberOfLines != maximumNumberOfLines) {
|
||||
|
||||
@ -81,6 +81,10 @@ struct ASTextKitAttributes {
|
||||
The radius that should be applied to the shadow blur. Larger values mean a larger, more blurred shadow.
|
||||
*/
|
||||
CGFloat shadowRadius;
|
||||
/**
|
||||
The minimum scale that the textnode can apply to fit long words in constrained size.
|
||||
*/
|
||||
CGFloat minimumScaleFactor;
|
||||
/**
|
||||
A pointer to a function that that returns a custom layout manager subclass. If nil, defaults to NSLayoutManager.
|
||||
*/
|
||||
@ -103,6 +107,7 @@ struct ASTextKitAttributes {
|
||||
[shadowColor copy],
|
||||
shadowOpacity,
|
||||
shadowRadius,
|
||||
minimumScaleFactor,
|
||||
layoutManagerFactory
|
||||
};
|
||||
};
|
||||
@ -114,6 +119,7 @@ struct ASTextKitAttributes {
|
||||
&& maximumNumberOfLines == other.maximumNumberOfLines
|
||||
&& shadowOpacity == other.shadowOpacity
|
||||
&& shadowRadius == other.shadowRadius
|
||||
&& minimumScaleFactor == other.minimumScaleFactor
|
||||
&& layoutManagerFactory == other.layoutManagerFactory
|
||||
&& CGSizeEqualToSize(shadowOffset, other.shadowOffset)
|
||||
&& _objectsEqual(exclusionPaths, other.exclusionPaths)
|
||||
|
||||
19
AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.h
Normal file
19
AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.h
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// ASTextKitFontSizeAdjuster.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Luke on 1/20/16.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ASTextKitFontSizeAdjuster : NSObject
|
||||
|
||||
|
||||
- (instancetype)initWithContext:(ASTextKitContext *)context
|
||||
minimumScaleFactor:(CGFloat)minimumScaleFactor
|
||||
constrainedSize:(CGSize)constrainedSize;
|
||||
|
||||
- (void) adjustFontSize;
|
||||
@end
|
||||
100
AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.m
Normal file
100
AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.m
Normal file
@ -0,0 +1,100 @@
|
||||
//
|
||||
// ASTextKitFontSizeAdjuster.m
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Luke on 1/20/16.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASTextKitContext.h"
|
||||
#import "ASTextKitFontSizeAdjuster.h"
|
||||
|
||||
@implementation ASTextKitFontSizeAdjuster
|
||||
{
|
||||
__weak ASTextKitContext *_context;
|
||||
CGFloat _minimumScaleFactor;
|
||||
CGSize _constrainedSize;
|
||||
}
|
||||
|
||||
- (instancetype)initWithContext:(ASTextKitContext *)context
|
||||
minimumScaleFactor:(CGFloat)minimumScaleFactor
|
||||
constrainedSize:(CGSize)constrainedSize
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_context = context;
|
||||
_minimumScaleFactor = minimumScaleFactor;
|
||||
_constrainedSize = constrainedSize;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (CGSize)sizeForAttributedString:(NSAttributedString *)attrString
|
||||
{
|
||||
return [attrString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin
|
||||
context:nil].size;
|
||||
}
|
||||
|
||||
|
||||
- (void) adjustFontSizeForAttributeString:(NSMutableAttributedString *)attrString withScaleFactor:(CGFloat)scaleFactor
|
||||
{
|
||||
{
|
||||
[attrString beginEditing];
|
||||
|
||||
[attrString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attrString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
|
||||
|
||||
UIFont* font = value;
|
||||
font = [font fontWithSize:font.pointSize * scaleFactor];
|
||||
|
||||
[attrString removeAttribute:NSFontAttributeName range:range];
|
||||
[attrString addAttribute:NSFontAttributeName value:font range:range];
|
||||
}];
|
||||
|
||||
[attrString endEditing];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)adjustFontSize
|
||||
{
|
||||
if (_minimumScaleFactor <= 0 || _minimumScaleFactor >= 1) {
|
||||
return;
|
||||
}
|
||||
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
NSString *str = textStorage.string;
|
||||
NSArray *words = [str componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
NSString *longestWordNeedingResize = @"";
|
||||
for (NSString *word in words) {
|
||||
if ([word length] > [longestWordNeedingResize length]) {
|
||||
longestWordNeedingResize = word;
|
||||
}
|
||||
}
|
||||
|
||||
if ([longestWordNeedingResize length] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSRange range = [str rangeOfString:longestWordNeedingResize];
|
||||
NSMutableAttributedString *attrString = [textStorage attributedSubstringFromRange:range].mutableCopy;
|
||||
CGSize defaultSize = [self sizeForAttributedString:attrString];
|
||||
|
||||
if (defaultSize.width > _constrainedSize.width) {
|
||||
[attrString removeAttribute:NSParagraphStyleAttributeName range:NSMakeRange(0, [attrString length])];
|
||||
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
|
||||
context.minimumScaleFactor = _minimumScaleFactor;
|
||||
[attrString boundingRectWithSize:CGSizeMake(_constrainedSize.width, defaultSize.height)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin
|
||||
context:context];
|
||||
|
||||
[self adjustFontSizeForAttributeString:attrString withScaleFactor:context.actualScaleFactor];
|
||||
|
||||
if ([self sizeForAttributedString:attrString].width <= _constrainedSize.width) {
|
||||
[self adjustFontSizeForAttributeString:textStorage withScaleFactor:context.actualScaleFactor];
|
||||
NSLog(@"ASTextKitFontSizeAdjuster : adjusted \"%@\"to fontsize actualScaleFactor:%f", longestWordNeedingResize, context.actualScaleFactor);
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
@class ASTextKitContext;
|
||||
@class ASTextKitShadower;
|
||||
@class ASTextKitFontSizeAdjuster;
|
||||
@protocol ASTextKitTruncating;
|
||||
|
||||
/**
|
||||
@ -46,6 +47,8 @@
|
||||
|
||||
@property (nonatomic, strong, readonly) id<ASTextKitTruncating> truncater;
|
||||
|
||||
@property (nonatomic, strong, readonly) ASTextKitFontSizeAdjuster *fontSizeAdjuster;
|
||||
|
||||
@property (nonatomic, strong, readonly) ASTextKitShadower *shadower;
|
||||
|
||||
@property (nonatomic, assign, readonly) ASTextKitAttributes attributes;
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#import "ASTextKitContext.h"
|
||||
#import "ASTextKitShadower.h"
|
||||
#import "ASTextKitTailTruncater.h"
|
||||
#import "ASTextKitFontSizeAdjuster.h"
|
||||
#import "ASTextKitTruncating.h"
|
||||
|
||||
//#define LOG(...) NSLog(__VA_ARGS__)
|
||||
@ -37,7 +38,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
CGSize _calculatedSize;
|
||||
BOOL _sizeIsCalculated;
|
||||
}
|
||||
@synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater;
|
||||
@synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater, fontSizeAdjuster = _fontSizeAdjuster;
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
@ -76,6 +77,19 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
return _truncater;
|
||||
}
|
||||
|
||||
- (ASTextKitFontSizeAdjuster *)fontSizeAdjuster
|
||||
{
|
||||
if (!_fontSizeAdjuster) {
|
||||
ASTextKitAttributes attributes = _attributes;
|
||||
// We must inset the constrained size by the size of the shadower.
|
||||
CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize];
|
||||
_fontSizeAdjuster = [[ASTextKitFontSizeAdjuster alloc] initWithContext:[self context]
|
||||
minimumScaleFactor:attributes.minimumScaleFactor
|
||||
constrainedSize:shadowConstrainedSize];
|
||||
}
|
||||
return _fontSizeAdjuster;
|
||||
}
|
||||
|
||||
- (ASTextKitContext *)context
|
||||
{
|
||||
if (!_context) {
|
||||
@ -122,13 +136,16 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
|
||||
- (void)_calculateSize
|
||||
{
|
||||
if (_attributes.minimumScaleFactor < 1 && _attributes.minimumScaleFactor > 0) {
|
||||
[[self fontSizeAdjuster] adjustFontSize];
|
||||
}
|
||||
|
||||
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by
|
||||
// -usedRectForTextContainer:).
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
}];
|
||||
|
||||
|
||||
CGRect constrainedRect = {CGPointZero, _constrainedSize};
|
||||
__block CGRect boundingRect;
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user