Load and store URLPreviewViewData in RoomBubbleCellData.

Implement close button and store the action in Core Data. Hide the preview image view when no image is received. Remove line breaks in description text.
This commit is contained in:
Doug
2021-09-01 10:37:37 +01:00
parent b1bf11eb52
commit e6449c978c
28 changed files with 716 additions and 376 deletions
@@ -21,36 +21,49 @@ import Reusable
class URLPreviewView: UIView, NibLoadable, Themable {
// MARK: - Constants
private enum Constants { }
private static let sizingView = URLPreviewView.instantiate()
private enum Constants {
// URL Previews
static let maxHeight: CGFloat = 247.0
static let width: CGFloat = 267.0
}
// MARK: - Properties
var viewModel: URLPreviewViewModel! {
var preview: URLPreviewViewData? {
didSet {
viewModel.viewDelegate = self
guard let preview = preview else { return }
renderLoaded(preview)
}
}
weak var delegate: URLPreviewViewDelegate?
@IBOutlet weak var imageContainer: UIView!
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var faviconImageView: UIImageView!
@IBOutlet weak var closeButton: UIButton!
@IBOutlet weak var siteNameLabel: UILabel!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!
/// The constraint that pins the top of the text container to the top of the view.
@IBOutlet weak var textContainerViewConstraint: NSLayoutConstraint!
/// The constraint that pins the top of the text container to the bottom of the image container.
@IBOutlet weak var textContainerImageConstraint: NSLayoutConstraint!
override var intrinsicContentSize: CGSize {
CGSize(width: RoomBubbleCellLayout.urlPreviewViewWidth, height: RoomBubbleCellLayout.urlPreviewViewHeight)
CGSize(width: Constants.width, height: Constants.maxHeight)
}
// MARK: - Setup
static func instantiate(viewModel: URLPreviewViewModel) -> Self {
static func instantiate() -> Self {
let view = Self.loadFromNib()
view.update(theme: ThemeService.shared().theme)
view.viewModel = viewModel
viewModel.process(viewAction: .loadData)
return view
}
@@ -63,14 +76,10 @@ class URLPreviewView: UIView, NibLoadable, Themable {
layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
faviconImageView.layer.cornerRadius = 6
siteNameLabel.isUserInteractionEnabled = false
titleLabel.isUserInteractionEnabled = false
descriptionLabel.isUserInteractionEnabled = false
#warning("Debugging for previews - to be removed")
faviconImageView.backgroundColor = .systemBlue.withAlphaComponent(0.7)
}
// MARK: - Public
@@ -86,9 +95,19 @@ class URLPreviewView: UIView, NibLoadable, Themable {
descriptionLabel.textColor = theme.colors.secondaryContent
descriptionLabel.font = theme.fonts.caption1
let closeButtonAsset = ThemeService.shared().isCurrentThemeDark() ? Asset.Images.urlPreviewCloseDark : Asset.Images.urlPreviewClose
closeButton.setImage(closeButtonAsset.image, for: .normal)
}
static func contentViewHeight(for preview: URLPreviewViewData) -> CGFloat {
sizingView.renderLoaded(preview)
return sizingView.systemLayoutSizeFitting(sizingView.intrinsicContentSize).height
}
// MARK: - Private
#warning("Check whether we should show a loading state.")
private func renderLoading(_ url: URL) {
imageView.image = nil
@@ -98,48 +117,49 @@ class URLPreviewView: UIView, NibLoadable, Themable {
}
private func renderLoaded(_ preview: URLPreviewViewData) {
imageView.image = preview.image
if let image = preview.image {
imageView.image = image
showImageContainer()
} else {
imageView.image = nil
hideImageContainer()
}
siteNameLabel.text = preview.siteName ?? preview.url.host
titleLabel.text = preview.title
descriptionLabel.text = preview.text
}
private func renderError(_ error: Error) {
imageView.image = nil
private func showImageContainer() {
// When the image container has a superview it is already visible
guard imageContainer.superview == nil else { return }
siteNameLabel.text = "Error"
titleLabel.text = descriptionLabel.text
descriptionLabel.text = error.localizedDescription
textContainerViewConstraint.isActive = false
addSubview(imageContainer)
textContainerImageConstraint.isActive = true
// Ensure the close button remains visible
bringSubviewToFront(closeButton)
}
private func hideImageContainer() {
textContainerImageConstraint.isActive = false
imageContainer.removeFromSuperview()
textContainerViewConstraint.isActive = true
}
// MARK: - Action
@IBAction private func openURL(_ sender: Any) {
MXLog.debug("[URLPreviewView] Link was tapped.")
viewModel.process(viewAction: .openURL)
guard let preview = preview else { return }
// Ask the delegate to open the URL for the event, as the bubble component
// has the original un-sanitized URL that needs to be opened.
delegate?.didOpenURLFromPreviewView(self, for: preview.eventID, in: preview.roomID)
}
@IBAction private func close(_ sender: Any) {
}
}
// MARK: URLPreviewViewModelViewDelegate
extension URLPreviewView: URLPreviewViewModelViewDelegate {
func urlPreviewViewModel(_ viewModel: URLPreviewViewModelType, didUpdateViewState viewState: URLPreviewViewState) {
DispatchQueue.main.async {
switch viewState {
case .loading(let url):
self.renderLoading(url)
case .loaded(let preview):
self.renderLoaded(preview)
case .error(let error):
self.renderError(error)
case .hidden:
self.frame.size.height = 0
}
}
guard let preview = preview else { return }
delegate?.didCloseURLPreviewView(self, for: preview.eventID, in: preview.roomID)
}
}