parent
1fb1b5bbe2
commit
5f2b38c50b
|
@ -302,6 +302,7 @@
|
|||
45360B901F9527DA00FA666C /* SearcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45360B8F1F9527DA00FA666C /* SearcherTest.swift */; };
|
||||
45360B911F952AA900FA666C /* MarqueeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */; };
|
||||
4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4539B5851F79348F007141FF /* PushRegistrationManager.swift */; };
|
||||
4542DF52208B82E9007B4E76 /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadModel.swift */; };
|
||||
45464DBC1DFA041F001D3FD6 /* DataChannelMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */; };
|
||||
454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 454A84032059C787008B8C75 /* MediaTileViewController.swift */; };
|
||||
454A965A1FD6017E008D2A0E /* SignalAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D913491F62D4A500722898 /* SignalAttachment.swift */; };
|
||||
|
@ -919,6 +920,7 @@
|
|||
45360B8F1F9527DA00FA666C /* SearcherTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearcherTest.swift; sourceTree = "<group>"; };
|
||||
4539B5851F79348F007141FF /* PushRegistrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushRegistrationManager.swift; sourceTree = "<group>"; };
|
||||
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
4542DF51208B82E9007B4E76 /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
|
||||
45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataChannelMessage.swift; sourceTree = "<group>"; };
|
||||
454A84032059C787008B8C75 /* MediaTileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaTileViewController.swift; sourceTree = "<group>"; };
|
||||
454A965E1FD60EA2008D2A0E /* OWSFlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSFlatButton.swift; path = SignalMessaging/Views/OWSFlatButton.swift; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1898,6 +1900,7 @@
|
|||
45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */,
|
||||
458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */,
|
||||
458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */,
|
||||
4542DF51208B82E9007B4E76 /* ThreadModel.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3199,6 +3202,7 @@
|
|||
34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */,
|
||||
34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */,
|
||||
340FC8B8204DAC8D007AEB0F /* AddToGroupViewController.m in Sources */,
|
||||
4542DF52208B82E9007B4E76 /* ThreadModel.swift in Sources */,
|
||||
341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */,
|
||||
340FC8AF204DAC8D007AEB0F /* OWSLinkDeviceViewController.m in Sources */,
|
||||
34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */,
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
public class ThreadModel: NSObject {
|
||||
let hasUnreadMessages: Bool
|
||||
let lastMessageDate: Date
|
||||
let isGroupThread: Bool
|
||||
let threadRecord: TSThread
|
||||
let unreadCount: UInt
|
||||
let contactIdentifier: String?
|
||||
let name: String
|
||||
let isMuted: Bool
|
||||
var isContactThread: Bool {
|
||||
return !isGroupThread
|
||||
}
|
||||
|
||||
let lastMessageText: String?
|
||||
|
||||
// func attributedSnippet(blockedPhoneNumberSet: Set<String>) {
|
||||
// let isBlocked: Bool = {
|
||||
// guard let contactIdentifier = self.contactIdentifier else {
|
||||
// return false
|
||||
// }
|
||||
// assert(isContactThread)
|
||||
// return blockedPhoneNumberSet.contains(self.contactIdentifier)
|
||||
// }()
|
||||
//
|
||||
//
|
||||
//
|
||||
//// BOOL hasUnreadMessages = thread.hasUnreadMessages;
|
||||
//
|
||||
//// NSMutableAttributedString *snippetText = [NSMutableAttributedString new];
|
||||
// var snippetText = NSMutableAttributedString()
|
||||
// if isBlocked {
|
||||
// // If thread is blocked, don't show a snippet or mute status.
|
||||
// let append = NSAttributedString(string: NSLocalizedString("HOME_VIEW_BLOCKED_CONTACT_CONVERSATION",
|
||||
// comment: "A label for conversations with blocked users."),
|
||||
// attributes: <#T##[String : Any]?#>)
|
||||
//
|
||||
//// if (isBlocked) {
|
||||
//// // If thread is blocked, don't show a snippet or mute status.
|
||||
//// [snippetText
|
||||
//// appendAttributedString:[[NSAttributedString alloc]
|
||||
//// initWithString:NSLocalizedString(@"HOME_VIEW_BLOCKED_CONTACT_CONVERSATION",
|
||||
//// @"A label for conversations with blocked users.")
|
||||
//// attributes:@{
|
||||
//// NSFontAttributeName : self.snippetFont.ows_mediumWeight,
|
||||
//// NSForegroundColorAttributeName : [UIColor ows_blackColor],
|
||||
//// }]];
|
||||
//// } else {
|
||||
//// if ([thread isMuted]) {
|
||||
//// [snippetText appendAttributedString:[[NSAttributedString alloc]
|
||||
//// initWithString:@"\ue067 "
|
||||
//// attributes:@{
|
||||
//// NSFontAttributeName : [UIFont ows_elegantIconsFont:9.f],
|
||||
//// NSForegroundColorAttributeName : (hasUnreadMessages
|
||||
//// ? [UIColor colorWithWhite:0.1f alpha:1.f]
|
||||
//// : [UIColor lightGrayColor]),
|
||||
//// }]];
|
||||
//// }
|
||||
//// NSString *displayableText = thread.lastMessageText;
|
||||
//// if (displayableText) {
|
||||
//// [snippetText appendAttributedString:[[NSAttributedString alloc]
|
||||
//// initWithString:displayableText
|
||||
//// attributes:@{
|
||||
//// NSFontAttributeName :
|
||||
//// (hasUnreadMessages ? self.snippetFont.ows_mediumWeight
|
||||
//// : self.snippetFont),
|
||||
//// NSForegroundColorAttributeName :
|
||||
//// (hasUnreadMessages ? [UIColor ows_blackColor]
|
||||
//// : [UIColor lightGrayColor]),
|
||||
//// }]];
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// return snippetText;
|
||||
// }
|
||||
// }
|
||||
|
||||
init(thread: TSThread, transaction: YapDatabaseReadTransaction) {
|
||||
self.threadRecord = thread
|
||||
self.lastMessageDate = thread.lastMessageDate()
|
||||
self.isGroupThread = thread.isGroupThread()
|
||||
self.name = thread.name()
|
||||
self.isMuted = thread.isMuted
|
||||
self.lastMessageText = thread.lastMessageText(transaction: transaction)
|
||||
|
||||
if let contactThread = thread as? TSContactThread {
|
||||
self.contactIdentifier = contactThread.contactIdentifier()
|
||||
} else {
|
||||
self.contactIdentifier = nil
|
||||
}
|
||||
|
||||
self.unreadCount = thread.unreadMessageCount(transaction: transaction)
|
||||
self.hasUnreadMessages = unreadCount > 0
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSContactsManager;
|
||||
@class TSThread;
|
||||
@class ThreadModel;
|
||||
@class YapDatabaseReadTransaction;
|
||||
|
||||
@interface HomeViewCell : UITableViewCell
|
||||
|
@ -14,10 +14,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (NSString *)cellReuseIdentifier;
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread
|
||||
- (void)configureWithThread:(ThreadModel *)thread
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
|
||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#import <SignalServiceKit/OWSMessageManager.h>
|
||||
#import <SignalServiceKit/TSContactThread.h>
|
||||
#import <SignalServiceKit/TSGroupThread.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -27,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic) UIView *unreadBadge;
|
||||
@property (nonatomic) UILabel *unreadLabel;
|
||||
|
||||
@property (nonatomic, nullable) TSThread *thread;
|
||||
@property (nonatomic, nullable) ThreadModel *thread;
|
||||
@property (nonatomic, nullable) OWSContactsManager *contactsManager;
|
||||
|
||||
@property (nonatomic, readonly) NSMutableArray<NSLayoutConstraint *> *viewConstraints;
|
||||
|
@ -143,16 +142,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return NSStringFromClass(self.class);
|
||||
}
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
- (void)configureWithThread:(ThreadModel *)thread
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssert(thread);
|
||||
OWSAssert(contactsManager);
|
||||
OWSAssert(blockedPhoneNumberSet);
|
||||
OWSAssert(transaction);
|
||||
|
||||
self.thread = thread;
|
||||
self.contactsManager = contactsManager;
|
||||
|
@ -173,9 +170,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// changes to the dynamic type settings are reflected.
|
||||
self.snippetLabel.font = [self snippetFont];
|
||||
self.snippetLabel.attributedText =
|
||||
[self attributedSnippetForThread:thread
|
||||
blockedPhoneNumberSet:blockedPhoneNumberSet
|
||||
transaction:transaction];
|
||||
[self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet];
|
||||
|
||||
self.dateTimeLabel.text = [self stringForDate:thread.lastMessageDate];
|
||||
|
||||
|
@ -187,7 +182,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.dateTimeLabel.font = self.unreadFont;
|
||||
}
|
||||
|
||||
NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread];
|
||||
NSUInteger unreadCount = thread.unreadCount;
|
||||
if (unreadCount == 0) {
|
||||
[self.viewConstraints addObject:[self.payloadView autoPinTrailingToSuperviewMargin]];
|
||||
} else {
|
||||
|
@ -244,20 +239,20 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return;
|
||||
}
|
||||
|
||||
TSThread *thread = self.thread;
|
||||
ThreadModel *thread = self.thread;
|
||||
if (thread == nil) {
|
||||
OWSFail(@"%@ thread should not be nil", self.logTag);
|
||||
self.avatarView.image = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
self.avatarView.image =
|
||||
[OWSAvatarBuilder buildImageForThread:thread diameter:self.avatarSize contactsManager:contactsManager];
|
||||
self.avatarView.image = [OWSAvatarBuilder buildImageForThread:thread.threadRecord
|
||||
diameter:self.avatarSize
|
||||
contactsManager:contactsManager];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedSnippetForThread:(TSThread *)thread
|
||||
- (NSAttributedString *)attributedSnippetForThread:(ThreadModel *)thread
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(thread);
|
||||
|
||||
|
@ -290,7 +285,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
: [UIColor lightGrayColor]),
|
||||
}]];
|
||||
}
|
||||
NSString *displayableText = [thread lastMessageLabelWithTransaction:transaction];
|
||||
NSString *displayableText = thread.lastMessageText;
|
||||
if (displayableText) {
|
||||
[snippetText appendAttributedString:[[NSAttributedString alloc]
|
||||
initWithString:displayableText
|
||||
|
@ -452,7 +447,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
self.nameLabel.font = self.nameFont;
|
||||
|
||||
TSThread *thread = self.thread;
|
||||
ThreadModel *thread = self.thread;
|
||||
if (thread == nil) {
|
||||
OWSFail(@"%@ thread should not be nil", self.logTag);
|
||||
self.nameLabel.attributedText = nil;
|
||||
|
|
|
@ -600,15 +600,16 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
HomeViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:HomeViewCell.cellReuseIdentifier];
|
||||
OWSAssert(cell);
|
||||
|
||||
TSThread *thread = [self threadForIndexPath:indexPath];
|
||||
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
|
||||
[cell configureWithThread:thread
|
||||
contactsManager:self.contactsManager
|
||||
blockedPhoneNumberSet:self.blockedPhoneNumberSet
|
||||
transaction:transaction];
|
||||
TSThread *threadRecord = [self threadForIndexPath:indexPath];
|
||||
__block ThreadModel *thread;
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
thread = [[ThreadModel alloc] initWithThread:threadRecord transaction:transaction];
|
||||
}];
|
||||
|
||||
[cell configureWithThread:thread
|
||||
contactsManager:self.contactsManager
|
||||
blockedPhoneNumberSet:self.blockedPhoneNumberSet];
|
||||
|
||||
if ((unsigned long)indexPath.row == [self.threadMappings numberOfItemsInSection:0] - 1) {
|
||||
cell.separatorInset = UIEdgeInsetsMake(0.f, cell.bounds.size.width, 0.f, 0.f);
|
||||
}
|
||||
|
@ -969,7 +970,12 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
|
||||
// If the user hasn't already granted contact access
|
||||
// we don't want to request until they receive a message.
|
||||
if ([TSThread numberOfKeysInCollection] > 0) {
|
||||
__block BOOL hasAnyMessages;
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction];
|
||||
}];
|
||||
|
||||
if (hasAnyMessages) {
|
||||
[self.contactsManager requestSystemContactsOnce];
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*
|
||||
* @return YES if it has unread TSIncomingMessages, NO otherwise.
|
||||
*/
|
||||
- (BOOL)hasUnreadMessages;
|
||||
- (BOOL)hasUnreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
NS_SWIFT_NAME(hasUnreadMessages(transaction:));
|
||||
- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
NS_SWIFT_NAME(unreadMessageCount(transaction:));
|
||||
|
||||
- (BOOL)hasSafetyNumbers;
|
||||
|
||||
|
@ -90,7 +93,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*
|
||||
* @return Thread preview string.
|
||||
*/
|
||||
- (NSString *)lastMessageLabelWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
- (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
NS_SWIFT_NAME(lastMessageText(transaction:));
|
||||
|
||||
/**
|
||||
* Updates the thread's caches of the latest interaction.
|
||||
|
|
|
@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic, copy) NSString *messageDraft;
|
||||
@property (atomic, nullable) NSDate *mutedUntilDate;
|
||||
|
||||
- (TSInteraction *)lastInteraction;
|
||||
- (TSInteraction *)lastInteractionWithTranscation:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -200,8 +200,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return count;
|
||||
}
|
||||
|
||||
- (BOOL)hasUnreadMessages {
|
||||
TSInteraction *interaction = self.lastInteraction;
|
||||
- (BOOL)hasUnreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
TSInteraction *interaction = [self lastInteractionWithTransaction:transaction];
|
||||
BOOL hasUnread = NO;
|
||||
|
||||
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
|
||||
|
@ -229,6 +230,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return [messages copy];
|
||||
}
|
||||
|
||||
- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:self.uniqueId];
|
||||
}
|
||||
|
||||
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {
|
||||
|
@ -239,12 +245,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSAssert([self unseenMessagesWithTransaction:transaction].count < 1);
|
||||
}
|
||||
|
||||
- (TSInteraction *) lastInteraction {
|
||||
__block TSInteraction *last;
|
||||
[OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
last = [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId];
|
||||
}];
|
||||
return last;
|
||||
- (TSInteraction *)lastInteractionWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId];
|
||||
}
|
||||
|
||||
- (TSInteraction *)lastInteractionForInboxWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
|
@ -276,7 +279,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
- (NSString *)lastMessageLabelWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
- (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
TSInteraction *interaction = [self lastInteractionForInboxWithTransaction:transaction];
|
||||
if ([interaction conformsToProtocol:@protocol(OWSPreviewText)]) {
|
||||
|
|
|
@ -114,6 +114,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return !![[OWSIdentityManager sharedManager] identityKeyForRecipientId:self.contactIdentifier];
|
||||
}
|
||||
|
||||
// TODO deprecate this? seems weird to access the displayName in the DB model
|
||||
- (NSString *)name
|
||||
{
|
||||
return [[TextSecureKitEnv sharedEnv].contactsManager displayNameForPhoneIdentifier:self.contactIdentifier];
|
||||
|
|
|
@ -15,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (NSUInteger)unreadMessagesCount;
|
||||
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread;
|
||||
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread;
|
||||
|
||||
- (void)updateApplicationBadgeCount;
|
||||
|
||||
|
|
|
@ -95,14 +95,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[CurrentAppContext() setMainAppBadgeNumber:numberOfItems];
|
||||
}
|
||||
|
||||
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread
|
||||
{
|
||||
__block NSUInteger numberOfItems;
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId];
|
||||
}];
|
||||
return numberOfItems;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
Loading…
Reference in New Issue