Merge branch 'charlesmchen/moreSystemMessages'
This commit is contained in:
commit
320dfd89e3
@ -67,14 +67,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
= (TSUnreadIndicatorInteraction *)((TSMessageAdapter *)messageData).interaction;
|
||||
return [self sizeForUnreadIndicator:interaction cacheKey:cacheKey layout:layout];
|
||||
}
|
||||
case TSIncomingMessageAdapter:
|
||||
case TSOutgoingMessageAdapter:
|
||||
break;
|
||||
default:
|
||||
// TODO: we need to examine the other cases.
|
||||
OWSFail(@"---- Unknown sizing interaction: %@", [((TSMessageAdapter *)messageData).interaction class]);
|
||||
break;
|
||||
}
|
||||
} else if ([messageData isKindOfClass:[OWSCall class]]) {
|
||||
id cacheKey = [self cacheKeyForMessageData:messageData];
|
||||
TSInteraction *interaction = ((OWSCall *)messageData).interaction;
|
||||
return [self sizeForSystemMessage:interaction cacheKey:cacheKey layout:layout];
|
||||
} else {
|
||||
OWSFail(@"Can't size unknown message data type: %@", [messageData class]);
|
||||
}
|
||||
|
||||
// BEGIN HACK iOS10EmojiBug see: https://github.com/WhisperSystems/Signal-iOS/issues/1368
|
||||
|
||||
@ -27,4 +27,8 @@ import Foundation
|
||||
static let rejectedCallWithUnseenIdentityChangeNotificationBody = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY", comment: "notification action")
|
||||
static let rejectedCallWithUnseenIdentityChangeNotificationBodyWithoutCallerName = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITHOUT_CALLER_NAME", comment: "notification action")
|
||||
static let rejectedCallWithUnseenIdentityChangeNotificationBodyWithCallerName = NSLocalizedString("MISSED_CALL_WITH_UNSEEN_IDENTITY_BODY_WITH_CALLER_NAME", comment: "notification action")
|
||||
|
||||
static let callBackAlertTitle = NSLocalizedString("CALL_USER_ALERT_TITLE", comment: "Title for alert offering to call a user.")
|
||||
static let callBackAlertMessageFormat = NSLocalizedString("CALL_USER_ALERT_MESSAGE_FORMAT", comment: "Message format for alert offering to call a user. Embeds {{the user's display name or phone number}}.")
|
||||
static let callBackAlertCallButton = NSLocalizedString("CALL_USER_ALERT_CALL_BUTTON", comment: "Label for call button for alert offering to call a user.")
|
||||
}
|
||||
|
||||
@ -151,6 +151,7 @@ typedef enum : NSUInteger {
|
||||
JSQMessagesComposerTextViewPasteDelegate,
|
||||
OWSConversationSettingsViewDelegate,
|
||||
OWSMessagesCollectionViewFlowLayoutDelegate,
|
||||
OWSSystemMessageCellDelegate,
|
||||
OWSTextViewPasteDelegate,
|
||||
OWSVoiceMemoGestureDelegate,
|
||||
UIDocumentMenuDelegate,
|
||||
@ -1578,6 +1579,7 @@ typedef enum : NSUInteger {
|
||||
[self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSSystemMessageCell cellReuseIdentifier]
|
||||
forIndexPath:indexPath];
|
||||
[cell configureWithInteraction:interaction];
|
||||
cell.systemMessageCellDelegate = self;
|
||||
|
||||
return cell;
|
||||
}
|
||||
@ -1795,7 +1797,6 @@ typedef enum : NSUInteger {
|
||||
[self showConversationSettings];
|
||||
}
|
||||
|
||||
|
||||
- (void)collectionView:(JSQMessagesCollectionView *)collectionView
|
||||
didTapMessageBubbleAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
@ -1968,13 +1969,10 @@ typedef enum : NSUInteger {
|
||||
}
|
||||
} break;
|
||||
case TSErrorMessageAdapter:
|
||||
[self handleErrorMessageTap:(TSErrorMessage *)interaction];
|
||||
break;
|
||||
case TSInfoMessageAdapter:
|
||||
[self handleInfoMessageTap:(TSInfoMessage *)interaction];
|
||||
break;
|
||||
case TSCallAdapter:
|
||||
case TSUnreadIndicatorAdapter:
|
||||
OWSFail(@"Unexpected tap for system message.");
|
||||
break;
|
||||
default:
|
||||
DDLogDebug(@"Unhandled bubble touch for interaction: %@.", interaction);
|
||||
@ -2218,17 +2216,38 @@ typedef enum : NSUInteger {
|
||||
|
||||
- (void)handleErrorMessageTap:(TSErrorMessage *)message
|
||||
{
|
||||
if ([message isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) {
|
||||
[self tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)message];
|
||||
} else if ([message isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]) {
|
||||
[self tappedUnknownContactBlockOfferMessage:(OWSUnknownContactBlockOfferMessage *)message];
|
||||
} else if (message.errorType == TSErrorMessageInvalidMessage) {
|
||||
[self tappedCorruptedMessage:message];
|
||||
} else if (message.errorType == TSErrorMessageNonBlockingIdentityChange) {
|
||||
[self tappedNonBlockingIdentityChangeForRecipientId:message.recipientId];
|
||||
} else {
|
||||
DDLogWarn(@"%@ Unhandled tap for error message:%@", self.tag, message);
|
||||
OWSAssert(message);
|
||||
|
||||
switch (message.errorType) {
|
||||
case TSErrorMessageInvalidKeyException:
|
||||
break;
|
||||
case TSErrorMessageNonBlockingIdentityChange:
|
||||
[self tappedNonBlockingIdentityChangeForRecipientId:message.recipientId];
|
||||
return;
|
||||
case TSErrorMessageWrongTrustedIdentityKey:
|
||||
OWSAssert([message isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]);
|
||||
[self tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)message];
|
||||
return;
|
||||
case TSErrorMessageMissingKeyId:
|
||||
// Unused.
|
||||
break;
|
||||
case TSErrorMessageNoSession:
|
||||
break;
|
||||
case TSErrorMessageInvalidMessage:
|
||||
[self tappedCorruptedMessage:message];
|
||||
return;
|
||||
case TSErrorMessageDuplicateMessage:
|
||||
// Unused.
|
||||
break;
|
||||
case TSErrorMessageInvalidVersion:
|
||||
break;
|
||||
case TSErrorMessageUnknownContactBlockOffer:
|
||||
OWSAssert([message isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]);
|
||||
[self tappedUnknownContactBlockOfferMessage:(OWSUnknownContactBlockOfferMessage *)message];
|
||||
return;
|
||||
}
|
||||
|
||||
DDLogWarn(@"%@ Unhandled tap for error message:%@", self.tag, message);
|
||||
}
|
||||
|
||||
- (void)tappedNonBlockingIdentityChangeForRecipientId:(NSString *)signalId
|
||||
@ -2251,16 +2270,35 @@ typedef enum : NSUInteger {
|
||||
|
||||
- (void)handleInfoMessageTap:(TSInfoMessage *)message
|
||||
{
|
||||
if ([message isKindOfClass:[OWSAddToContactsOfferMessage class]]) {
|
||||
[self tappedAddToContactsOfferMessage:(OWSAddToContactsOfferMessage *)message];
|
||||
} else {
|
||||
DDLogInfo(@"%@ Unhandled tap for info message:%@", self.tag, message);
|
||||
OWSAssert(message);
|
||||
|
||||
switch (message.messageType) {
|
||||
case TSInfoMessageUserNotRegistered:
|
||||
break;
|
||||
case TSInfoMessageTypeSessionDidEnd:
|
||||
break;
|
||||
case TSInfoMessageTypeUnsupportedMessage:
|
||||
// Unused.
|
||||
break;
|
||||
case TSInfoMessageAddToContactsOffer:
|
||||
OWSAssert([message isKindOfClass:[OWSAddToContactsOfferMessage class]]);
|
||||
[self tappedAddToContactsOfferMessage:(OWSAddToContactsOfferMessage *)message];
|
||||
return;
|
||||
case TSInfoMessageTypeGroupUpdate:
|
||||
[self showConversationSettings];
|
||||
return;
|
||||
case TSInfoMessageTypeGroupQuit:
|
||||
break;
|
||||
case TSInfoMessageTypeDisappearingMessagesUpdate:
|
||||
[self showConversationSettings];
|
||||
return;
|
||||
}
|
||||
|
||||
DDLogInfo(@"%@ Unhandled tap for info message:%@", self.tag, message);
|
||||
}
|
||||
|
||||
- (void)tappedCorruptedMessage:(TSErrorMessage *)message
|
||||
{
|
||||
|
||||
NSString *alertMessage = [NSString
|
||||
stringWithFormat:NSLocalizedString(@"CORRUPTED_SESSION_DESCRIPTION", @"ActionSheet title"), self.thread.name];
|
||||
|
||||
@ -2394,6 +2432,59 @@ typedef enum : NSUInteger {
|
||||
editImmediately:YES];
|
||||
}
|
||||
|
||||
- (void)handleCallTap:(TSCall *)call
|
||||
{
|
||||
OWSAssert(call);
|
||||
|
||||
if (![self.thread isKindOfClass:[TSContactThread class]]) {
|
||||
DDLogError(@"%@ unexpected thread: %@ in %s", self.tag, self.thread, __PRETTY_FUNCTION__);
|
||||
OWSAssert(NO);
|
||||
return;
|
||||
}
|
||||
|
||||
TSContactThread *contactThread = (TSContactThread *)self.thread;
|
||||
NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:contactThread.contactIdentifier];
|
||||
|
||||
UIAlertController *alertController = [UIAlertController
|
||||
alertControllerWithTitle:[CallStrings callBackAlertTitle]
|
||||
message:[NSString stringWithFormat:[CallStrings callBackAlertMessageFormat], displayName]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
__weak MessagesViewController *weakSelf = self;
|
||||
UIAlertAction *callAction = [UIAlertAction actionWithTitle:[CallStrings callBackAlertCallButton]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
[weakSelf callAction:nil];
|
||||
}];
|
||||
[alertController addAction:callAction];
|
||||
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil];
|
||||
[alertController addAction:dismissAction];
|
||||
|
||||
[[UIApplication sharedApplication].frontmostViewController presentViewController:alertController
|
||||
animated:YES
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - OWSSystemMessageCellDelegate
|
||||
|
||||
- (void)didTapSystemMessageWithInteraction:(TSInteraction *)interaction
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
OWSAssert(interaction);
|
||||
|
||||
if ([interaction isKindOfClass:[TSErrorMessage class]]) {
|
||||
[self handleErrorMessageTap:(TSErrorMessage *)interaction];
|
||||
} else if ([interaction isKindOfClass:[TSInfoMessage class]]) {
|
||||
[self handleInfoMessageTap:(TSInfoMessage *)interaction];
|
||||
} else if ([interaction isKindOfClass:[TSCall class]]) {
|
||||
[self handleCallTap:(TSCall *)interaction];
|
||||
} else {
|
||||
OWSFail(@"Tap for system messages of unknown type: %@", [interaction class]);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - ContactEditingDelegate
|
||||
|
||||
- (void)didFinishEditingContact
|
||||
|
||||
@ -7,8 +7,18 @@
|
||||
|
||||
@class TSInteraction;
|
||||
|
||||
@protocol OWSSystemMessageCellDelegate <NSObject>
|
||||
|
||||
- (void)didTapSystemMessageWithInteraction:(TSInteraction *)interaction;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface OWSSystemMessageCell : JSQMessagesCollectionViewCell
|
||||
|
||||
@property (nonatomic, weak) id<OWSSystemMessageCellDelegate> systemMessageCellDelegate;
|
||||
|
||||
@property (nonatomic, nullable, readonly) TSInteraction *interaction;
|
||||
|
||||
- (void)configureWithInteraction:(TSInteraction *)interaction;
|
||||
|
||||
@ -26,6 +26,47 @@
|
||||
|
||||
@implementation OWSSystemMessageCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self commontInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
[self commontInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)commontInit
|
||||
{
|
||||
OWSAssert(!self.imageView);
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
self.imageView = [UIImageView new];
|
||||
[self.contentView addSubview:self.imageView];
|
||||
|
||||
self.titleLabel = [UILabel new];
|
||||
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
|
||||
self.titleLabel.font = [OWSSystemMessageCell titleFont];
|
||||
self.titleLabel.numberOfLines = 0;
|
||||
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
[self.contentView addSubview:self.titleLabel];
|
||||
|
||||
UITapGestureRecognizer *tap =
|
||||
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
||||
[self addGestureRecognizer:tap];
|
||||
}
|
||||
|
||||
+ (NSString *)cellReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
@ -37,20 +78,6 @@
|
||||
|
||||
_interaction = interaction;
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
if (!self.titleLabel) {
|
||||
self.imageView = [UIImageView new];
|
||||
[self.contentView addSubview:self.imageView];
|
||||
|
||||
self.titleLabel = [UILabel new];
|
||||
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
|
||||
self.titleLabel.font = [OWSSystemMessageCell titleFont];
|
||||
self.titleLabel.numberOfLines = 0;
|
||||
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
[self.contentView addSubview:self.titleLabel];
|
||||
}
|
||||
|
||||
UIImage *icon = [self iconForInteraction:self.interaction];
|
||||
self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
self.imageView.tintColor = [self iconColorForInteraction:self.interaction];
|
||||
@ -213,4 +240,13 @@
|
||||
self.interaction = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Gesture recognizers
|
||||
|
||||
- (void)handleTapGesture:(UITapGestureRecognizer *)tap
|
||||
{
|
||||
OWSAssert(self.interaction);
|
||||
|
||||
[self.systemMessageCellDelegate didTapSystemMessageWithInteraction:self.interaction];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
@property (nonatomic, nullable, readonly) TSUnreadIndicatorInteraction *interaction;
|
||||
|
||||
- (void)configureWithInteraction:(TSUnreadIndicatorInteraction *)interaction;
|
||||
;
|
||||
|
||||
+ (CGSize)cellSizeForInteraction:(TSUnreadIndicatorInteraction *)interaction
|
||||
collectionViewWidth:(CGFloat)collectionViewWidth;
|
||||
|
||||
@ -27,6 +27,64 @@
|
||||
|
||||
@implementation OWSUnreadIndicatorCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self commontInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
[self commontInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)commontInit
|
||||
{
|
||||
OWSAssert(!self.bannerView);
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
self.bannerView = [UIView new];
|
||||
self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3];
|
||||
[self.contentView addSubview:self.bannerView];
|
||||
|
||||
self.bannerTopHighlightView = [UIView new];
|
||||
self.bannerTopHighlightView.backgroundColor = [UIColor colorWithRGBHex:0xf9f3eb];
|
||||
[self.bannerView addSubview:self.bannerTopHighlightView];
|
||||
|
||||
self.bannerBottomHighlightView1 = [UIView new];
|
||||
self.bannerBottomHighlightView1.backgroundColor = [UIColor colorWithRGBHex:0xd1c6b8];
|
||||
[self.bannerView addSubview:self.bannerBottomHighlightView1];
|
||||
|
||||
self.bannerBottomHighlightView2 = [UIView new];
|
||||
self.bannerBottomHighlightView2.backgroundColor = [UIColor colorWithRGBHex:0xdbcfc0];
|
||||
[self.bannerView addSubview:self.bannerBottomHighlightView2];
|
||||
|
||||
self.titleLabel = [UILabel new];
|
||||
self.titleLabel.text = [OWSUnreadIndicatorCell titleForInteraction:self.interaction];
|
||||
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
|
||||
self.titleLabel.font = [OWSUnreadIndicatorCell titleFont];
|
||||
[self.bannerView addSubview:self.titleLabel];
|
||||
|
||||
self.subtitleLabel = [UILabel new];
|
||||
self.subtitleLabel.text = [OWSUnreadIndicatorCell subtitleForInteraction:self.interaction];
|
||||
self.subtitleLabel.textColor = [UIColor ows_infoMessageBorderColor];
|
||||
self.subtitleLabel.font = [OWSUnreadIndicatorCell subtitleFont];
|
||||
self.subtitleLabel.numberOfLines = 0;
|
||||
self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.contentView addSubview:self.subtitleLabel];
|
||||
}
|
||||
|
||||
+ (NSString *)cellReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
@ -40,38 +98,7 @@
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
if (!self.titleLabel) {
|
||||
self.bannerView = [UIView new];
|
||||
self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3];
|
||||
[self.contentView addSubview:self.bannerView];
|
||||
|
||||
self.bannerTopHighlightView = [UIView new];
|
||||
self.bannerTopHighlightView.backgroundColor = [UIColor colorWithRGBHex:0xf9f3eb];
|
||||
[self.bannerView addSubview:self.bannerTopHighlightView];
|
||||
|
||||
self.bannerBottomHighlightView1 = [UIView new];
|
||||
self.bannerBottomHighlightView1.backgroundColor = [UIColor colorWithRGBHex:0xd1c6b8];
|
||||
[self.bannerView addSubview:self.bannerBottomHighlightView1];
|
||||
|
||||
self.bannerBottomHighlightView2 = [UIView new];
|
||||
self.bannerBottomHighlightView2.backgroundColor = [UIColor colorWithRGBHex:0xdbcfc0];
|
||||
[self.bannerView addSubview:self.bannerBottomHighlightView2];
|
||||
|
||||
self.titleLabel = [UILabel new];
|
||||
self.titleLabel.text = [OWSUnreadIndicatorCell titleForInteraction:self.interaction];
|
||||
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
|
||||
self.titleLabel.font = [OWSUnreadIndicatorCell titleFont];
|
||||
[self.bannerView addSubview:self.titleLabel];
|
||||
|
||||
self.subtitleLabel = [UILabel new];
|
||||
self.subtitleLabel.text = [OWSUnreadIndicatorCell subtitleForInteraction:self.interaction];
|
||||
self.subtitleLabel.textColor = [UIColor ows_infoMessageBorderColor];
|
||||
self.subtitleLabel.font = [OWSUnreadIndicatorCell subtitleFont];
|
||||
self.subtitleLabel.numberOfLines = 0;
|
||||
self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.contentView addSubview:self.subtitleLabel];
|
||||
}
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
+ (UIFont *)titleFont
|
||||
|
||||
@ -199,6 +199,15 @@
|
||||
/* embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call' */
|
||||
"CALL_STATUS_FORMAT" = "Signal %@";
|
||||
|
||||
/* Label for call button for alert offering to call a user. */
|
||||
"CALL_USER_ALERT_CALL_BUTTON" = "Call";
|
||||
|
||||
/* Message format for alert offering to call a user. Embeds {{the user's display name or phone number}}. */
|
||||
"CALL_USER_ALERT_MESSAGE_FORMAT" = "Would you like to call %@?";
|
||||
|
||||
/* Title for alert offering to call a user. */
|
||||
"CALL_USER_ALERT_TITLE" = "Call?";
|
||||
|
||||
/* Reminder to the user of the benefits of enabling CallKit and disabling CallKit privacy. */
|
||||
"CALL_VIEW_SETTINGS_NAG_DESCRIPTION_ALL" = "You can answer calls directly from your lock screen and see the name and phone number for incoming calls if you change your settings.\n\nSee the privacy settings for details.";
|
||||
|
||||
@ -1063,7 +1072,7 @@
|
||||
/* button title to confirm adding a recipient to a group when their safety number has recently changed */
|
||||
"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION" = "Confirm and Add to Group";
|
||||
|
||||
/* button title to confirm calling a recipient whose safety number recently changed */
|
||||
/* alert button text to confirm placing an outgoing call after the recipients Safety Number has changed. */
|
||||
"SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION" = "Confirm and Call";
|
||||
|
||||
/* button title to confirm sending to a recipient whose safety number recently changed */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user