mirror of https://github.com/oxen-io/session-ios
New downloading progress view (#2006)
Replace previous "scary" warning-style attachment notifications with something less alarming. Includes file name and file type emoji when discernable. // FREEBIEpull/1/head
parent
67e94e2b55
commit
d9e3e87735
@ -0,0 +1,110 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
* Represents a download-in-progress
|
||||
*/
|
||||
class AttachmentPointerAdapter: JSQMediaItem, OWSMessageEditing {
|
||||
|
||||
let TAG = "[AttachmentPointerAdapter]"
|
||||
let isIncoming: Bool
|
||||
let attachmentPointer: TSAttachmentPointer
|
||||
var cachedView: UIView?
|
||||
var attachmentPointerView: AttachmentPointerView?
|
||||
|
||||
required init(attachmentPointer: TSAttachmentPointer, isIncoming: Bool) {
|
||||
self.isIncoming = isIncoming
|
||||
self.attachmentPointer = attachmentPointer
|
||||
super.init(maskAsOutgoing: !isIncoming)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
assertionFailure("init(coder:) has not been implemented")
|
||||
self.isIncoming = true
|
||||
self.attachmentPointer = TSAttachmentPointer()
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
func canPerformAction(_ action: Selector) -> Bool {
|
||||
// No actions can be performed on a downloading attachment.
|
||||
return false
|
||||
}
|
||||
|
||||
func performAction(_ action: Selector) {
|
||||
// Should not get here, as you can't perform any actions on a downloading attachment.
|
||||
Logger.error("\(TAG) unexpectedly trying to perform action: \(action) on downloading attachment.")
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
override func mediaViewDisplaySize() -> CGSize {
|
||||
return CGSize(width: 200, height: 90)
|
||||
}
|
||||
|
||||
override func mediaView() -> UIView? {
|
||||
guard self.cachedView == nil else {
|
||||
return self.cachedView
|
||||
}
|
||||
|
||||
let frame = CGRect(origin: CGPoint.zero, size: self.mediaViewDisplaySize())
|
||||
let view = UIView(frame: frame)
|
||||
self.cachedView = view
|
||||
|
||||
JSQMessagesMediaViewBubbleImageMasker.applyBubbleImageMask(toMediaView: view, isOutgoing:!isIncoming)
|
||||
|
||||
view.isUserInteractionEnabled = false
|
||||
view.clipsToBounds = true
|
||||
|
||||
let attachmentPointerView = AttachmentPointerView(attachmentPointer: attachmentPointer, isIncoming: self.isIncoming)
|
||||
self.attachmentPointerView = attachmentPointerView
|
||||
view.addSubview(attachmentPointerView)
|
||||
|
||||
attachmentPointerView.autoPinWidthToSuperview(withMargin: 20)
|
||||
attachmentPointerView.autoVCenterInSuperview()
|
||||
|
||||
switch attachmentPointer.state {
|
||||
case .downloading:
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(attachmentDownloadProgress), name: NSNotification.Name.attachmentDownloadProgress, object: nil)
|
||||
view.backgroundColor = isIncoming ? UIColor.jsq_messageBubbleLightGray() : UIColor.ows_fadedBlue()
|
||||
case .enqueued:
|
||||
view.backgroundColor = isIncoming ? UIColor.jsq_messageBubbleLightGray() : UIColor.ows_fadedBlue()
|
||||
case .failed:
|
||||
view.backgroundColor = UIColor.gray
|
||||
}
|
||||
|
||||
return cachedView
|
||||
}
|
||||
|
||||
func attachmentDownloadProgress(_ notification: NSNotification) {
|
||||
guard let attachmentPointerView = self.attachmentPointerView else {
|
||||
Logger.error("\(TAG) downloading view was unexpectedly nil for notification: \(notification)")
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
guard let userInfo = notification.userInfo else {
|
||||
Logger.error("\(TAG) user info was unexpectedly nil for notification: \(notification)")
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
guard let progress = userInfo[kAttachmentDownloadProgressKey] as? CGFloat else {
|
||||
Logger.error("\(TAG) missing progress measure for notification user info: \(userInfo)")
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
guard let attachmentId = userInfo[kAttachmentDownloadAttachmentIDKey] as? String else {
|
||||
Logger.error("\(TAG) missing attachmentId for notification user info: \(userInfo)")
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
if (self.attachmentPointer.uniqueId == attachmentId) {
|
||||
attachmentPointerView.progress = progress
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class AttachmentPointerView: UIView {
|
||||
|
||||
let TAG = "[AttachmentPointerView]"
|
||||
|
||||
let progressView = OWSProgressView()
|
||||
let nameLabel = UILabel()
|
||||
let statusLabel = UILabel()
|
||||
let isIncoming: Bool
|
||||
let filename: String
|
||||
let attachmentPointer: TSAttachmentPointer
|
||||
let genericFilename = NSLocalizedString("ATTACHMENT_DOWNLOADING_DEFAULT_ATTACHMENT_LABEL", comment: "Generic name label when downloading an attachment with no known name.")
|
||||
|
||||
var progress: CGFloat = 0 {
|
||||
didSet {
|
||||
self.progressView.progress = progress
|
||||
}
|
||||
}
|
||||
|
||||
required init(attachmentPointer: TSAttachmentPointer, isIncoming: Bool) {
|
||||
self.isIncoming = isIncoming
|
||||
self.attachmentPointer = attachmentPointer
|
||||
|
||||
let attachmentPointerFilename = attachmentPointer.filename
|
||||
if let filename = attachmentPointerFilename, !filename.isEmpty {
|
||||
self.filename = filename
|
||||
} else {
|
||||
self.filename = genericFilename
|
||||
}
|
||||
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
createSubviews()
|
||||
updateViews()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
override init(frame: CGRect) {
|
||||
assertionFailure()
|
||||
// This initializer should never be called, but we assign some bogus values to keep the compiler happy.
|
||||
self.filename = genericFilename
|
||||
self.isIncoming = false
|
||||
self.attachmentPointer = TSAttachmentPointer()
|
||||
super.init(frame: frame)
|
||||
self.createSubviews()
|
||||
self.updateViews()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
assertionFailure()
|
||||
// This initializer should never be called, but we assign some bogus values to keep the compiler happy.
|
||||
self.filename = genericFilename
|
||||
self.isIncoming = false
|
||||
self.attachmentPointer = TSAttachmentPointer()
|
||||
super.init(coder: aDecoder)
|
||||
self.createSubviews()
|
||||
self.updateViews()
|
||||
}
|
||||
|
||||
func createSubviews() {
|
||||
self.addSubview(nameLabel)
|
||||
// truncate middle to be sure we include file extension
|
||||
nameLabel.lineBreakMode = .byTruncatingMiddle
|
||||
nameLabel.textAlignment = .center
|
||||
|
||||
nameLabel.textColor = self.textColor
|
||||
nameLabel.font = UIFont.ows_dynamicTypeBody()
|
||||
|
||||
nameLabel.autoPinWidthToSuperview()
|
||||
nameLabel.autoPinEdge(toSuperviewEdge: .top)
|
||||
|
||||
self.addSubview(progressView)
|
||||
progressView.autoPinWidthToSuperview()
|
||||
progressView.autoPinEdge(.top, to: .bottom, of: nameLabel, withOffset: 6)
|
||||
progressView.autoSetDimension(.height, toSize: 8)
|
||||
|
||||
self.addSubview(statusLabel)
|
||||
statusLabel.textAlignment = .center
|
||||
statusLabel.adjustsFontSizeToFitWidth = true
|
||||
|
||||
statusLabel.textColor = self.textColor
|
||||
statusLabel.font = UIFont.ows_footnote()
|
||||
|
||||
statusLabel.autoPinWidthToSuperview()
|
||||
statusLabel.autoPinEdge(.top, to: .bottom, of: progressView, withOffset: 4)
|
||||
statusLabel.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
}
|
||||
|
||||
func emojiForContentType(_ contentType: String) -> String {
|
||||
if MIMETypeUtil.isImage(contentType) {
|
||||
return "📷"
|
||||
} else if MIMETypeUtil.isVideo(contentType) {
|
||||
return "📽"
|
||||
} else if MIMETypeUtil.isAudio(contentType) {
|
||||
return "📻"
|
||||
} else if MIMETypeUtil.isAnimated(contentType) {
|
||||
return "🎡"
|
||||
} else {
|
||||
// generic file
|
||||
return "📁"
|
||||
}
|
||||
}
|
||||
|
||||
func updateViews() {
|
||||
let emoji = self.emojiForContentType(self.attachmentPointer.contentType)
|
||||
nameLabel.text = "\(emoji) \(self.filename)"
|
||||
|
||||
statusLabel.text = {
|
||||
switch self.attachmentPointer.state {
|
||||
case .enqueued:
|
||||
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_QUEUED", comment: "Status label when an attachment is enqueued, but hasn't yet started downloading")
|
||||
case .downloading:
|
||||
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_IN_PROGRESS", comment: "Status label when an attachment is currently downloading")
|
||||
case .failed:
|
||||
return NSLocalizedString("ATTACHMENT_DOWNLOADING_STATUS_FAILED", comment: "Status label when an attachment download has failed.")
|
||||
}
|
||||
}()
|
||||
|
||||
if attachmentPointer.state == .downloading {
|
||||
progressView.isHidden = false
|
||||
} else {
|
||||
progressView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
var textColor: UIColor {
|
||||
return self.isIncoming ? UIColor.darkText : UIColor.white
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue