Commit dfa4fedd authored by Kateryna Kostiuk's avatar Kateryna Kostiuk

UI: outgoing transfer

Change-Id: I9757e6d542f798d374ae012fda57b9fe07b94647
parent e7623b65
......@@ -125,6 +125,7 @@
0E4909751FEAC943005CAA50 /* CallViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E4909741FEAC943005CAA50 /* CallViewController.storyboard */; };
0E49097A1FEAC9E1005CAA50 /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4909791FEAC9E1005CAA50 /* CallViewController.swift */; };
0E49097C1FEACA4B005CAA50 /* CallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */; };
0E5A668322F0B1F100AA6820 /* ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5A668222F0B1F100AA6820 /* ProgressView.swift */; };
0E68571120238546008B0717 /* ConversationNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E68571020238546008B0717 /* ConversationNavigation.swift */; };
0E6949791FA7E71C0029B60A /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6949781FA7E71C0029B60A /* BaseViewController.swift */; };
0E6F544D223BFE3E00ECC3CE /* DisposableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6F544C223BFE3E00ECC3CE /* DisposableCell.swift */; };
......@@ -449,6 +450,7 @@
0E4909741FEAC943005CAA50 /* CallViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CallViewController.storyboard; sourceTree = "<group>"; };
0E4909791FEAC9E1005CAA50 /* CallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = "<group>"; };
0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewModel.swift; sourceTree = "<group>"; };
0E5A668222F0B1F100AA6820 /* ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ProgressView.swift; path = "../../../../../../../../../System/Volumes/Data/Users/kkostiuk/Desktop/iphone/ring-project/client-ios/Ring/Ring/UI/ProgressView.swift"; sourceTree = "<group>"; };
0E639459224AB32200C0890A /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; };
0E63F1F3202907090001F248 /* Ring.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ring.entitlements; sourceTree = "<group>"; };
0E68571020238546008B0717 /* ConversationNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationNavigation.swift; sourceTree = "<group>"; };
......@@ -913,6 +915,7 @@
56AC650D1E85694D00EA1AA9 /* DesignableTextField.swift */,
1A2041901F1FD46200C08435 /* DesignableView.swift */,
0E438A99204F47E700402900 /* SettingsTableView.swift */,
0E5A668222F0B1F100AA6820 /* ProgressView.swift */,
);
path = UI;
sourceTree = "<group>";
......@@ -2055,6 +2058,7 @@
0EF78DE31FD0AE3000FC6966 /* ConversationsManager.swift in Sources */,
0EAA9DB42029F0AA005E245C /* ProxyCell.swift in Sources */,
66266FC021557D2F002757A6 /* ScanViewModel.swift in Sources */,
0E5A668322F0B1F100AA6820 /* ProgressView.swift in Sources */,
0E96ED79225D06480016C07D /* GeneralSettingsViewModel.swift in Sources */,
1A2041801F1E903B00C08435 /* CreateProfileViewController.swift in Sources */,
66266FC4215C18F8002757A6 /* Emoji+Helpers.swift in Sources */,
......
......@@ -311,6 +311,8 @@ internal enum L10n {
internal static let readableStatusRefuse = L10n.tr("Localizable", "dataTransfer.readableStatusRefuse")
/// Complete
internal static let readableStatusSuccess = L10n.tr("Localizable", "dataTransfer.readableStatusSuccess")
/// Failed to send
internal static let sendingFailed = L10n.tr("Localizable", "dataTransfer.sendingFailed")
}
internal enum GeneralSettings {
......
......@@ -26,6 +26,7 @@ import RxSwift
import ActiveLabel
import SwiftyBeaver
// swiftlint:disable type_body_length
class MessageCell: UITableViewCell, NibReusable {
let log = SwiftyBeaver.self
......@@ -53,8 +54,10 @@ class MessageCell: UITableViewCell, NibReusable {
@IBOutlet weak var bubbleViewMask: UIView?
private var transferImageView = UIImageView()
private var transferProgressView = ProgressView()
var dataTransferProgressUpdater: Timer?
var outgoingImageProgressUpdater: Timer?
var disposeBag = DisposeBag()
......@@ -62,24 +65,38 @@ class MessageCell: UITableViewCell, NibReusable {
if self.sendingIndicator != nil {
self.sendingIndicator.stopAnimating()
}
super.prepareForReuse()
self.stopProgressMonitor()
self.stopOutgoingImageMonitor()
self.transferProgressView.removeFromSuperview()
self.disposeBag = DisposeBag()
super.prepareForReuse()
}
func startProgressMonitor(_ item: MessageViewModel,
_ conversationViewModel: ConversationViewModel) {
if self.outgoingImageProgressUpdater != nil {
self.stopOutgoingImageMonitor()
return
}
if self.dataTransferProgressUpdater != nil {
self.stopProgressMonitor()
return
}
guard let transferId = item.daemonId else { return }
self.dataTransferProgressUpdater = Timer.scheduledTimer(timeInterval: 0.5,
target: self,
selector: #selector(self.updateProgressBar),
userInfo: ["transferId": transferId,
"conversationViewModel": conversationViewModel],
repeats: true)
self.dataTransferProgressUpdater = Timer
.scheduledTimer(timeInterval: 0.5,
target: self,
selector: #selector(self.updateProgressBar),
userInfo: ["transferId": transferId,
"conversationViewModel": conversationViewModel],
repeats: true)
self.outgoingImageProgressUpdater = Timer
.scheduledTimer(timeInterval: 0.01,
target: self,
selector: #selector(self.updateOutgoigTransfer),
userInfo: ["transferId": transferId,
"conversationViewModel": conversationViewModel],
repeats: true)
}
func stopProgressMonitor() {
......@@ -88,6 +105,13 @@ class MessageCell: UITableViewCell, NibReusable {
self.dataTransferProgressUpdater = nil
}
func stopOutgoingImageMonitor() {
if let outgoingImageUpdater = self.outgoingImageProgressUpdater {
outgoingImageUpdater.invalidate()
self.outgoingImageProgressUpdater = nil
}
}
@objc func updateProgressBar(timer: Timer) {
guard let userInfoDict = timer.userInfo as? NSDictionary else { return }
guard let transferId = userInfoDict["transferId"] as? UInt64 else { return }
......@@ -99,6 +123,17 @@ class MessageCell: UITableViewCell, NibReusable {
}
}
@objc func updateOutgoigTransfer(timer: Timer) {
guard let userInfoDict = timer.userInfo as? NSDictionary else { return }
guard let transferId = userInfoDict["transferId"] as? UInt64 else { return }
guard let viewModel = userInfoDict["conversationViewModel"] as? ConversationViewModel else { return }
if let progress = viewModel.getTransferProgress(transferId: transferId) {
DispatchQueue.main.async {
self.transferProgressView.progress = CGFloat(progress * 100)
}
}
}
func showCopyMenu() {
becomeFirstResponder()
let menu = UIMenuController.shared
......@@ -253,7 +288,6 @@ class MessageCell: UITableViewCell, NibReusable {
func configureFromItem(_ conversationViewModel: ConversationViewModel,
_ items: [MessageViewModel]?,
cellForRowAt indexPath: IndexPath) {
self.backgroundColor = UIColor.clear
self.bubbleViewMask?.backgroundColor = UIColor.jamiMsgBackground
self.transferImageView.backgroundColor = UIColor.jamiMsgBackground
......@@ -415,8 +449,16 @@ class MessageCell: UITableViewCell, NibReusable {
self.transferImageView.translatesAutoresizingMaskIntoConstraints = true
self.transferImageView.topAnchor.constraint(equalTo: self.bubble.topAnchor, constant: 0).isActive = true
self.transferImageView.bottomAnchor.constraint(equalTo: self.bubble.bottomAnchor, constant: 0).isActive = true
if !message.message.incoming && message.initialTransferStatus != .success {
self.transferProgressView.frame = self.transferImageView.frame
self.transferProgressView.image = image
self.transferProgressView.status.value = message.initialTransferStatus
self.transferProgressView.progress = 0
self.transferProgressView.target = 100
self.transferProgressView.currentProgress = 0
self.bubble.addSubview(self.transferProgressView)
}
}
}
// swiftlint:enable cyclomatic_complexity
}
......@@ -223,6 +223,7 @@
"dataTransfer.readableStatusRefuse" = "Refuse";
"dataTransfer.readableStatusOngoing" = "Transferring";
"dataTransfer.readableStatusCanceled" = "Canceled";
"dataTransfer.sendingFailed" = "Failed to send";
"dataTransfer.readableStatusSuccess" = "Complete";
"dataTransfer.readableStatusAccept" = "Accept";
"dataTransfer.readableStatusCancel" = "Cancel";
......
......@@ -191,32 +191,36 @@ class ConversationsManager: MessagesAdapterDelegate {
}
func createTransferNotification(info: NSDataTransferInfo) {
if UIApplication.shared.applicationState == .active {
return
}
guard let account = self.accountsService.getAccount(fromAccountId: info.accountId), AccountModelHelper
.init(withAccount: account).isAccountRing() &&
accountsService.getCurrentProxyState(accountID: info.accountId) else {
DispatchQueue.main.async { [weak self] in
if UIApplication.shared.applicationState == .active {
return
}
var data = [String: String]()
var message = L10n.Notifications.newFile + " "
if let name = info.path.split(separator: "/").last {
message += name
} else {
message += info.path
}
data [NotificationUserInfoKeys.messageContent.rawValue] = message
data [NotificationUserInfoKeys.participantID.rawValue] = info.peer
data [NotificationUserInfoKeys.accountID.rawValue] = info.accountId
if let name = info.displayName {
data [NotificationUserInfoKeys.name.rawValue] = name
self.notificationHandler.presentMessageNotification(data: data)
} else {
guard let hash = JamiURI(schema: URIType.ring,
infoHach: info.peer).hash else {return}
}
DispatchQueue.global(qos: .background).async {
guard let account = self?.accountsService.getAccount(fromAccountId: info.accountId), AccountModelHelper
.init(withAccount: account).isAccountRing(),
let state = self?.accountsService.getCurrentProxyState(accountID: info.accountId), state else {
return
}
var data = [String: String]()
var message = L10n.Notifications.newFile + " "
if let name = info.path.split(separator: "/").last {
message += name
} else {
message += info.path
}
data [NotificationUserInfoKeys.messageContent.rawValue] = message
data [NotificationUserInfoKeys.participantID.rawValue] = info.peer
data [NotificationUserInfoKeys.accountID.rawValue] = info.accountId
if let name = info.displayName {
data [NotificationUserInfoKeys.name.rawValue] = name
self?.notificationHandler.presentMessageNotification(data: data)
} else {
guard let hash = JamiURI(schema: URIType.ring,
infoHach: info.peer).hash else {return}
searchNameAndPresentNotification(data: data, hash: hash)
self?.searchNameAndPresentNotification(data: data, hash: hash)
}
}
}
}
......
/*
* Copyright (C) 2019 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
import RxSwift
class ProgressView: UIView {
var maximumValue: CGFloat = 100
var imageView: UIImageView = UIImageView()
var statusLabel = UILabel()
var disposeBug = DisposeBag()
var status = Variable<DataTransferStatus>(.ongoing)
var progressVariable = Variable<CGFloat>(0)
lazy var statusLabelValue: Observable<String> = {
return Observable
.merge( status.asObservable().map({ status in
switch status {
case .created, .awaiting, .unknown:
return "0 %"
case .canceled:
return L10n.DataTransfer.readableStatusCanceled
case .error:
return L10n.DataTransfer.sendingFailed
default:
return ""
}
}),progressVariable
.asObservable()
.map({ progressValue in
return floor(progressValue)
.description.dropLast(2)
.description + " %"
}))
}()
var target: CGFloat = 100
var currentProgress: CGFloat = 0
var progress: CGFloat {
set (newProgress) {
target = newProgress
currentProgress += (target - currentProgress) * 0.1
innerProgress = currentProgress * toAngleScaler
self.progressVariable.value = newProgress
if self.bluredImage == nil {return}
setImage()
}
get {
return innerProgress / toAngleScaler
}
}
var image: UIImage? {
didSet {
guard let image = image else {return}
if bluredImage != nil {return}
guard let blureImage = self.blureImage(image: image) else {
return
}
self.bluredImage = blureImage
self.configureViews()
}
}
var bluredImage: UIImage?
// MARK: configure path
private var startPoint: CGPoint {
return CGPoint(x: self.bounds.size.width * 0.5, y: 0)
}
private var toAngleScaler: CGFloat {
return 360 / maximumValue
}
private var innerProgress: CGFloat = 0.0
var numberOfCornersInPath: Int {
return Int(floor((self.innerProgress + 45) / 90))
}
var currentPoint: CGPoint? {
let valueForSide = maximumValue / 4
let startValue = valueForSide / 2
let widthValue = self.bounds.size.width / valueForSide
let hightValue = self.bounds.size.height / valueForSide
let value = floor((progress + startValue - 1 ) / valueForSide)
switch value {
case 0 :
return CGPoint(x: widthValue * (progress + startValue), y: 0)
case 1:
return CGPoint(x: self.frame.width, y: hightValue * (progress - startValue))
case 2 :
return CGPoint(x: widthValue * (62.5 - progress), y: self.frame.height)
case 3:
return CGPoint(x: 0, y: hightValue * (87.5 - progress))
case 4:
return CGPoint(x: widthValue * (progress - 87.5), y: 0)
default:
return nil
}
}
func setImage() {
let ceneter = CGPoint(x: self.frame.width * 0.5, y: self.frame.height * 0.5)
let progresPath: UIBezierPath = UIBezierPath()
progresPath.move(to: startPoint)
guard let point = self.currentPoint else {return}
switch numberOfCornersInPath {
case 0:
progresPath.addLine(to: CGPoint(x: 0, y: 0))
progresPath.addLine(to: CGPoint(x: 0, y: self.frame.height))
progresPath.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
progresPath.addLine(to: CGPoint(x: self.frame.width, y: 0))
case 1:
progresPath.addLine(to: CGPoint(x: 0, y: 0))
progresPath.addLine(to: CGPoint(x: 0, y: self.frame.height))
progresPath.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
case 2:
progresPath.addLine(to: CGPoint(x: 0, y: 0))
progresPath.addLine(to: CGPoint(x: 0, y: self.frame.height))
case 3:
progresPath.addLine(to: CGPoint(x: 0, y: 0))
default:
break
}
progresPath.addLine(to: point)
progresPath.addLine(to: ceneter)
progresPath.close()
maskLayer.path = progresPath.cgPath
}
let maskLayer = CAShapeLayer.init()
override func removeFromSuperview() {
self.bluredImage = nil
self.disposeBug = DisposeBag()
self.progress = 0.00
self.target = 100
self.subviews.forEach { view in
view.removeFromSuperview()
}
super.removeFromSuperview()
}
func blureImage(image: UIImage) -> UIImage? {
let context = CIContext(options: nil)
let inputImage = CIImage(image: image)
let originalOrientation = image.imageOrientation
let originalScale = image.scale
let filterColor = CIFilter(name: "CIExposureAdjust")
filterColor?.setValue(-2, forKey: "inputEV")
filterColor?.setValue(inputImage, forKey: kCIInputImageKey)
let filterClamp = CIFilter(name: "CIAffineClamp")
let transform = CGAffineTransform.identity
filterClamp?.setValue(transform, forKey: "inputTransform")
filterClamp?.setValue(filterColor?.outputImage, forKey: kCIInputImageKey)
let filterGausse = CIFilter(name: "CIGaussianBlur")
filterGausse?.setValue(4.0, forKey: kCIInputRadiusKey)
filterGausse?.setValue(filterClamp?.outputImage, forKey: kCIInputImageKey)
let outputImage = filterGausse?.outputImage
var cgImage: CGImage?
guard let blurImage = outputImage else { return nil}
cgImage = context.createCGImage(blurImage, from: (inputImage?.extent)!)
guard let finaleImage = cgImage else { return nil }
return UIImage(cgImage: finaleImage, scale: originalScale, orientation: originalOrientation)
}
func configureViews() {
self.layer.cornerRadius = 20
self.layer.masksToBounds = true
self.imageView.frame = self.bounds
self.imageView.image = bluredImage
maskLayer.frame = self.bounds
self.imageView.layer.mask = maskLayer
self.addSubview(self.imageView)
statusLabel.frame = self.bounds
statusLabel.textAlignment = .center
statusLabel.textColor = UIColor.white
self.addSubview(statusLabel)
disposeBug = DisposeBag()
self.statusLabelValue
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak statusLabel] (text) in
statusLabel?.text = text
})
.disposed(by: self.disposeBug)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment