From bdf5a7e8596e59439399d4f9a7a43130901be314 Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 1 Jul 2021 18:57:05 +0800 Subject: [PATCH] fix: make link works in notification scene --- Mastodon.xcodeproj/project.pbxproj | 4 + .../xcschemes/xcschememanagement.plist | 4 +- .../Section/NotificationSection.swift | 8 +- .../StatusProvider/StatusProviderFacade.swift | 26 ---- .../Cell/AutoCompleteTableViewCell.swift | 8 +- .../Scene/Compose/ComposeViewController.swift | 144 +----------------- ...icationViewController+StatusProvider.swift | 65 ++++++++ .../NotificationViewController.swift | 20 ++- .../NotificationStatusTableViewCell.swift | 6 +- .../NotificationTableViewCell.swift | 28 ++-- 10 files changed, 113 insertions(+), 200 deletions(-) create mode 100644 Mastodon/Scene/Notification/NotificationViewController+StatusProvider.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 4bd06add1..682e2ea4a 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -296,6 +296,7 @@ DB6180F626391D580018D199 /* MediaPreviewableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6180F526391D580018D199 /* MediaPreviewableViewController.swift */; }; DB6180F826391D660018D199 /* MediaPreviewingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6180F726391D660018D199 /* MediaPreviewingViewController.swift */; }; DB6180FA26391F2E0018D199 /* MediaPreviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6180F926391F2E0018D199 /* MediaPreviewViewModel.swift */; }; + DB63BE7F268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB63BE7E268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift */; }; DB66728C25F9F8DC00D60309 /* ComposeViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66728B25F9F8DC00D60309 /* ComposeViewModel+Diffable.swift */; }; DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729525F9F91600D60309 /* ComposeStatusSection.swift */; }; DB66729C25F9F91F00D60309 /* ComposeStatusItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */; }; @@ -921,6 +922,7 @@ DB6180F526391D580018D199 /* MediaPreviewableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewableViewController.swift; sourceTree = ""; }; DB6180F726391D660018D199 /* MediaPreviewingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewingViewController.swift; sourceTree = ""; }; DB6180F926391F2E0018D199 /* MediaPreviewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewViewModel.swift; sourceTree = ""; }; + DB63BE7E268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationViewController+StatusProvider.swift"; sourceTree = ""; }; DB66728B25F9F8DC00D60309 /* ComposeViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ComposeViewModel+Diffable.swift"; sourceTree = ""; }; DB66729525F9F91600D60309 /* ComposeStatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusSection.swift; sourceTree = ""; }; DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusItem.swift; sourceTree = ""; }; @@ -2369,6 +2371,7 @@ isa = PBXGroup; children = ( DB9D6BF725E4F5690051B173 /* NotificationViewController.swift */, + DB63BE7E268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift */, 2D607AD726242FC500B70763 /* NotificationViewModel.swift */, 2D084B8C26258EA3003AA3AF /* NotificationViewModel+Diffable.swift */, 2D084B9226259545003AA3AF /* NotificationViewModel+LoadLatestState.swift */, @@ -3177,6 +3180,7 @@ 2D38F1F125CD477D00561493 /* HomeTimelineViewModel+LoadMiddleState.swift in Sources */, DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */, DB02CDAB26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift in Sources */, + DB63BE7F268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift in Sources */, DBB5255E2611F07A002F1F29 /* ProfileViewModel.swift in Sources */, DBAC648F267DC84D007FE9FD /* TableNodeDiffableDataSource.swift in Sources */, 2D8434FB25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift in Sources */, diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index da1d80e90..fdbacc128 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,7 +12,7 @@ CoreDataStack.xcscheme_^#shared#^_ orderHint - 21 + 27 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -37,7 +37,7 @@ NotificationService.xcscheme_^#shared#^_ orderHint - 20 + 21 SuppressBuildableAutocreation diff --git a/Mastodon/Diffiable/Section/NotificationSection.swift b/Mastodon/Diffiable/Section/NotificationSection.swift index 984e8a54e..fc88fddec 100644 --- a/Mastodon/Diffiable/Section/NotificationSection.swift +++ b/Mastodon/Diffiable/Section/NotificationSection.swift @@ -60,7 +60,7 @@ extension NotificationSection { statusItemAttribute: attribute ) cell.actionImageBackground.backgroundColor = color - cell.nameLabel.text = notification.account.displayName.isEmpty ? notification.account.username : notification.account.displayName + cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict) cell.actionLabel.text = actionText + " · " + timeText timestampUpdatePublisher .sink { [weak cell] _ in @@ -109,15 +109,15 @@ extension NotificationSection { .store(in: &cell.disposeBag) cell.actionImageBackground.backgroundColor = color cell.actionLabel.text = actionText + " · " + timeText - cell.nameLabel.text = notification.account.displayName.isEmpty ? notification.account.username : notification.account.displayName + cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict) if let url = notification.account.avatarImageURL() { - cell.avatatImageView.af.setImage( + cell.avatarImageView.af.setImage( withURL: url, placeholderImage: UIImage.placeholder(color: .systemFill), imageTransition: .crossDissolve(0.2) ) } - cell.avatatImageView.gesture().sink { [weak cell] _ in + cell.avatarImageView.gesture().sink { [weak cell] _ in cell?.delegate?.userAvatarDidPressed(notification: notification) } .store(in: &cell.disposeBag) diff --git a/Mastodon/Protocol/StatusProvider/StatusProviderFacade.swift b/Mastodon/Protocol/StatusProvider/StatusProviderFacade.swift index e6711bb1a..eff4ad124 100644 --- a/Mastodon/Protocol/StatusProvider/StatusProviderFacade.swift +++ b/Mastodon/Protocol/StatusProvider/StatusProviderFacade.swift @@ -523,32 +523,6 @@ extension StatusProviderFacade { extension StatusProviderFacade { - static func responseToStatusContentWarningRevealAction(dependency: NotificationViewController, cell: UITableViewCell) { - let status = Future { promise in - guard let diffableDataSource = dependency.viewModel.diffableDataSource, - let indexPath = dependency.tableView.indexPath(for: cell), - let item = diffableDataSource.itemIdentifier(for: indexPath) else { - promise(.success(nil)) - return - } - - switch item { - case .notification(let objectID, _): - dependency.viewModel.fetchedResultsController.managedObjectContext.perform { - let notification = dependency.viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! MastodonNotification - promise(.success(notification.status)) - } - default: - promise(.success(nil)) - } - } - - _responseToStatusContentWarningRevealAction( - dependency: dependency, - status: status - ) - } - static func responseToStatusContentWarningRevealAction(provider: StatusProvider, cell: UITableViewCell) { _responseToStatusContentWarningRevealAction( dependency: provider, diff --git a/Mastodon/Scene/Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift b/Mastodon/Scene/Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift index f629177dd..6b996ba1c 100644 --- a/Mastodon/Scene/Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift +++ b/Mastodon/Scene/Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift @@ -12,7 +12,7 @@ final class AutoCompleteTableViewCell: UITableViewCell { static let avatarImageSize = CGSize(width: 42, height: 42) static let avatarImageCornerRadius: CGFloat = 4 static let avatarToLabelSpacing: CGFloat = 12 - + let containerStackView: UIStackView = { let stackView = UIStackView() stackView.axis = .horizontal @@ -47,12 +47,6 @@ final class AutoCompleteTableViewCell: UITableViewCell { let separatorLine = UIView.separatorLine - override func prepareForReuse() { - super.prepareForReuse() - avatarImageView.af.cancelImageRequest() - avatarImageView.kf.cancelDownloadTask() - } - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) _init() diff --git a/Mastodon/Scene/Compose/ComposeViewController.swift b/Mastodon/Scene/Compose/ComposeViewController.swift index d3b83f98f..2d0c7f4c1 100644 --- a/Mastodon/Scene/Compose/ComposeViewController.swift +++ b/Mastodon/Scene/Compose/ComposeViewController.swift @@ -9,12 +9,11 @@ import os.log import UIKit import Combine import PhotosUI -import Kingfisher import MastodonSDK -import TwitterTextEditor import MetaTextView import MastodonMeta import Meta +import Nuke final class ComposeViewController: UIViewController, NeedsDependency { @@ -788,147 +787,6 @@ extension ComposeViewController: UITextViewDelegate { } -// MARK: - TextEditorViewTextAttributesDelegate -extension ComposeViewController: TextEditorViewTextAttributesDelegate { - - func textEditorView( - _ textEditorView: TextEditorView, - updateAttributedString attributedString: NSAttributedString, - completion: @escaping (NSAttributedString?) -> Void - ) { - // FIXME: needs O(1) update completion to fix performance issue - DispatchQueue.global().async { - let string = attributedString.string - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: update: %s", ((#file as NSString).lastPathComponent), #line, #function, string) - - let stringRange = NSRange(location: 0, length: string.length) - let highlightMatches = string.matches(pattern: MastodonRegex.highlightPattern) - let emojiMatches = string.matches(pattern: MastodonRegex.emojiPattern) - // only accept http/https scheme - let urlMatches = string.matches(pattern: "(?i)https?://\\S+(?:/|\\b)") - - DispatchQueue.main.async { [weak self] in - guard let self = self else { - completion(nil) - return - } - let customEmojiViewModel = self.viewModel.customEmojiViewModel.value - for view in self.suffixedAttachmentViews { - view.removeFromSuperview() - } - self.suffixedAttachmentViews.removeAll() - - // set normal appearance - let attributedString = NSMutableAttributedString(attributedString: attributedString) - attributedString.removeAttribute(.suffixedAttachment, range: stringRange) - attributedString.removeAttribute(.underlineStyle, range: stringRange) - attributedString.addAttribute(.foregroundColor, value: Asset.Colors.Label.primary.color, range: stringRange) - attributedString.addAttribute(.font, value: UIFont.preferredFont(forTextStyle: .body), range: stringRange) - - // hashtag - for match in highlightMatches { - // set highlight - var attributes = [NSAttributedString.Key: Any]() - attributes[.foregroundColor] = Asset.Colors.brandBlue.color - - // See `traitCollectionDidChange(_:)` - // set accessibility - if #available(iOS 13.0, *) { - switch self.traitCollection.accessibilityContrast { - case .high: - attributes[.underlineStyle] = NSUnderlineStyle.single.rawValue - default: - break - } - } - attributedString.addAttributes(attributes, range: match.range) - } - - // emoji - if let customEmojiViewModel = customEmojiViewModel, !customEmojiViewModel.emojiDict.value.isEmpty { - for match in emojiMatches { - guard let name = string.substring(with: match, at: 2) else { continue } - guard let emoji = customEmojiViewModel.emoji(shortcode: name) else { continue } - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: handle emoji: %s", ((#file as NSString).lastPathComponent), #line, #function, name) - - // set emoji token invisible (without upper bounce space) - var attributes = [NSAttributedString.Key: Any]() - attributes[.font] = UIFont.systemFont(ofSize: 0.01) - attributedString.addAttributes(attributes, range: match.range) - - // append emoji attachment - let imageViewSize = CGSize(width: 20, height: 20) - let imageView = UIImageView(frame: CGRect(origin: .zero, size: imageViewSize)) - textEditorView.textContentView.addSubview(imageView) - self.suffixedAttachmentViews.append(imageView) - let processor = DownsamplingImageProcessor(size: imageViewSize) - imageView.kf.setImage( - with: URL(string: emoji.url), - placeholder: UIImage.placeholder(size: imageViewSize, color: .systemFill), - options: [ - .processor(processor), - .scaleFactor(textEditorView.traitCollection.displayScale), - ], completionHandler: nil - ) - let layoutInTextContainer = { [weak textEditorView] (view: UIView, frame: CGRect) in - // `textEditorView` retains `textStorage`, which retains this block as a part of attributes. - guard let textEditorView = textEditorView else { - return - } - let insets = textEditorView.textContentInsets - view.frame = frame.offsetBy(dx: insets.left, dy: insets.top) - } - let attachment = TextAttributes.SuffixedAttachment( - size: imageViewSize, - attachment: .view(view: imageView, layoutInTextContainer: layoutInTextContainer) - ) - let index = match.range.upperBound - 1 - attributedString.addAttribute( - .suffixedAttachment, - value: attachment, - range: NSRange(location: index, length: 1) - ) - } - } - - // url - for match in urlMatches { - guard let name = string.substring(with: match, at: 0) else { continue } - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: handle emoji: %s", ((#file as NSString).lastPathComponent), #line, #function, name) - - // set highlight - var attributes = [NSAttributedString.Key: Any]() - attributes[.foregroundColor] = Asset.Colors.brandBlue.color - - // See `traitCollectionDidChange(_:)` - // set accessibility - if #available(iOS 13.0, *) { - switch self.traitCollection.accessibilityContrast { - case .high: - attributes[.underlineStyle] = NSUnderlineStyle.single.rawValue - default: - break - } - } - attributedString.addAttributes(attributes, range: match.range) - } - - if string.count > ComposeViewModel.composeContentLimit { - var attributes = [NSAttributedString.Key: Any]() - attributes[.foregroundColor] = Asset.Colors.danger.color - let boundStart = string.index(string.startIndex, offsetBy: ComposeViewModel.composeContentLimit) - let boundEnd = string.endIndex - let range = boundStart.. Future { + return Future { promise in + promise(.success(nil)) + } + } + + func status(for cell: UITableViewCell?, indexPath: IndexPath?) -> Future { + return Future { promise in + guard let cell = cell, + let diffableDataSource = self.viewModel.diffableDataSource, + let indexPath = self.tableView.indexPath(for: cell), + let item = diffableDataSource.itemIdentifier(for: indexPath) else { + promise(.success(nil)) + return + } + + switch item { + case .notification(let objectID, _): + self.viewModel.fetchedResultsController.managedObjectContext.perform { + let notification = self.viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! MastodonNotification + promise(.success(notification.status)) + } + default: + promise(.success(nil)) + } + } + } + + func status(for cell: UICollectionViewCell) -> Future { + return Future { promise in + promise(.success(nil)) + } + } + + var managedObjectContext: NSManagedObjectContext { + viewModel.fetchedResultsController.managedObjectContext + } + + var tableViewDiffableDataSource: UITableViewDiffableDataSource? { + return nil + } + + func item(for cell: UITableViewCell?, indexPath: IndexPath?) -> Item? { + return nil + } + + func items(indexPaths: [IndexPath]) -> [Item] { + return [] + } + + +} diff --git a/Mastodon/Scene/Notification/NotificationViewController.swift b/Mastodon/Scene/Notification/NotificationViewController.swift index 6004c48df..1a1672bf8 100644 --- a/Mastodon/Scene/Notification/NotificationViewController.swift +++ b/Mastodon/Scene/Notification/NotificationViewController.swift @@ -12,6 +12,8 @@ import GameplayKit import MastodonSDK import OSLog import UIKit +import Meta +import MetaTextView final class NotificationViewController: UIViewController, NeedsDependency { weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } @@ -271,6 +273,7 @@ extension NotificationViewController: ContentOffsetAdjustableTimelineViewControl // MARK: - NotificationTableViewCellDelegate extension NotificationViewController: NotificationTableViewCellDelegate { + func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) { viewModel.acceptFollowRequest(notification: notification) } @@ -286,20 +289,31 @@ extension NotificationViewController: NotificationTableViewCellDelegate { } } + func userNameLabelDidPressed(notification: MastodonNotification) { + let viewModel = CachedProfileViewModel(context: context, mastodonUser: notification.account) + DispatchQueue.main.async { + self.coordinator.present(scene: .profile(viewModel: viewModel), from: self, transition: .show) + } + } + func parent() -> UIViewController { self } func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, revealContentWarningButtonDidPressed button: UIButton) { - StatusProviderFacade.responseToStatusContentWarningRevealAction(dependency: self, cell: cell) + StatusProviderFacade.responseToStatusContentWarningRevealAction(provider: self, cell: cell) } func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { - StatusProviderFacade.responseToStatusContentWarningRevealAction(dependency: self, cell: cell) + StatusProviderFacade.responseToStatusContentWarningRevealAction(provider: self, cell: cell) } func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { - StatusProviderFacade.responseToStatusContentWarningRevealAction(dependency: self, cell: cell) + StatusProviderFacade.responseToStatusContentWarningRevealAction(provider: self, cell: cell) + } + + func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta) { + StatusProviderFacade.responseToStatusMetaTextAction(provider: self, cell: cell, metaText: metaText, didSelectMeta: meta) } } diff --git a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift index 3fb93d89c..f4138f092 100644 --- a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift +++ b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift @@ -57,8 +57,8 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell { return label }() - let nameLabel: UILabel = { - let label = UILabel() + let nameLabel: ActiveLabel = { + let label = ActiveLabel(style: .statusName) label.textColor = Asset.Colors.brandBlue.color label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold), maximumPointSize: 20) label.lineBreakMode = .byTruncatingTail @@ -259,7 +259,7 @@ extension NotificationStatusTableViewCell: StatusViewDelegate { } func statusView(_ statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta) { - // do nothing + delegate?.notificationStatusTableViewCell(self, statusView: statusView, metaText: metaText, didSelectMeta: meta) } } diff --git a/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift b/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift index fc69155be..375e4c2b3 100644 --- a/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift +++ b/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift @@ -9,6 +9,9 @@ import Combine import CoreDataStack import Foundation import UIKit +import Meta +import MetaTextView +import ActiveLabel protocol NotificationTableViewCellDelegate: AnyObject { var context: AppContext! { get } @@ -16,13 +19,14 @@ protocol NotificationTableViewCellDelegate: AnyObject { func parent() -> UIViewController func userAvatarDidPressed(notification: MastodonNotification) + func userNameLabelDidPressed(notification: MastodonNotification) func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, revealContentWarningButtonDidPressed button: UIButton) func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) - + func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta) + func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) - func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton) } @@ -34,7 +38,7 @@ final class NotificationTableViewCell: UITableViewCell { var delegate: NotificationTableViewCellDelegate? - let avatatImageView: UIImageView = { + let avatarImageView: UIImageView = { let imageView = UIImageView() imageView.layer.cornerRadius = 4 imageView.layer.cornerCurve = .continuous @@ -72,8 +76,8 @@ final class NotificationTableViewCell: UITableViewCell { return label }() - let nameLabel: UILabel = { - let label = UILabel() + let nameLabel: ActiveLabel = { + let label = ActiveLabel() label.textColor = Asset.Colors.brandBlue.color label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold), maximumPointSize: 20) label.lineBreakMode = .byTruncatingTail @@ -108,7 +112,7 @@ final class NotificationTableViewCell: UITableViewCell { override func prepareForReuse() { super.prepareForReuse() - avatatImageView.af.cancelImageRequest() + avatarImageView.af.cancelImageRequest() disposeBag.removeAll() } @@ -153,13 +157,13 @@ extension NotificationTableViewCell { avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1) ]) - avatarContainer.addSubview(avatatImageView) - avatatImageView.translatesAutoresizingMaskIntoConstraints = false + avatarContainer.addSubview(avatarImageView) + avatarImageView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - avatatImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1), - avatatImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1), - avatatImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor), - avatatImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor) + avatarImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1), + avatarImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1), + avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor), + avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor) ]) avatarContainer.addSubview(actionImageBackground)