// // CommonViewCard.swift // PayCash // // Created by Igor on 21.02.2021. // Copyright © 2021 AM. All rights reserved. // import UIKit import SkeletonView extension Common.Model.Menu { static func cardsTokensEmpty() -> Common.Model.Menu { .menu(image: Asset.commonToken.image.tinted(with: Asset.deepWater.color), title: L10n.Common.Card.Token.title) } } extension CommonMenuAction { static func cards( collection: [Common.Model.Menu]? = nil, icon: UIImage? = nil, tokens: [Network.Model.Token] = [], selectedMenu: Common.Model.Menu? = nil, selected: Network.Model.Token? = nil, empty: Common.Model.Menu? = .cardsTokensEmpty(), title: String? = L10n.Common.Card.Token.title, _ completion: ((Common.Model.Menu) -> Void)? = nil ) -> CommonMenuSelect { .select( icon: tokens.isEmpty ? nil : icon ?? Asset.commonMenuHorizontal.image, kind: .cards, title: title, empty: empty, collection: collection ?? tokens.map({ $0.toMenu() }), selected: selectedMenu ?? selected?.toMenu(), completion: completion ) } } class CommonViewCard: CommonControlCustom, CommonMenuSender { enum Style: String { case `default` case data } @IBInspectable var styleName: String = Style.default.rawValue { didSet { update(animated: false) } } var style: Style { get { Style(rawValue: styleName) ?? .default } set { styleName = newValue.rawValue } } @IBInspectable var image: UIImage? { didSet { update(animated: false) } } @IBInspectable var lzHeader: String? { didSet { update(animated: false) } } @IBInspectable var lzSubheader: String? { didSet { update(animated: false) } } @IBInspectable var lzData: String? { didSet { update(animated: false) } } @IBInspectable var icon: UIImage? { didSet { update(animated: false) } } @IBInspectable var badge: String? { didSet { update(animated: false) } } @IBInspectable var badgeColor: UIColor? { didSet { update(animated: false) } } var action: CommonMenuAction? { didSet { update(animated: false) } } var middleAction: (() -> Void)? = nil var rightAction: (() -> Void)? = nil @IBOutlet private weak var shadowContainer: CommonViewContainer! @IBOutlet private weak var indicator: UIActivityIndicatorView! @IBOutlet private weak var imgShadowView: UIImageView! @IBOutlet private weak var imgView: UIImageView! @IBOutlet private weak var imgViewOffset: NSLayoutConstraint! @IBOutlet private weak var imgViewWidth: NSLayoutConstraint! @IBOutlet private weak var headerLbl: UILabel! @IBOutlet private weak var badgeBtn: UIButton! @IBOutlet private weak var subheaderLbl: UILabel! @IBOutlet private weak var subheaderOffset: NSLayoutConstraint! @IBOutlet private weak var dataLbl: UILabel! @IBOutlet private weak var dataStackView: UIStackView! @IBOutlet private weak var dataOffset: NSLayoutConstraint! @IBOutlet private weak var actionBtn: UIButton! @IBOutlet private weak var bottomStackView: UIStackView! init(action: CommonMenuAction? = nil) { super.init(frame: .zero) self.action = action update(animated: false) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } var cashBillsService: CashBillsService? private var bills: [CashBillsService.Bill] { cashBillsService?.collection.filter({ $0.gtoken_out.quantity.symbol == action?.menu?.title }) ?? [] } var selectedCashBill: CashBillsService.Bill? // { // didSet { // update(animated: true) // } // } var selectCashBillAction: ((CashBillsService.Bill?) -> ())? var root: Bool = false private func generateRichSublabel(certCount: String, lockedCertCount: String) { dataStackView.arrangedSubviews.forEach({ $0.removeFromSuperview() }) dataStackView.alignment = .center // dataStackView.addHeightConstraint(height: 24) let label1 = UILabel() label1.attributedText = certCount.attributed(style: .bold, size: 20, color: Asset.textCoal.color) let label2 = UILabel() label2.attributedText = "+\(lockedCertCount)".attributed(style: .bold, size: 20, color: Asset.textBrick.color) dataStackView.addArrangedSubview(UIStackView(subviews: [label1, label2], axis: .horizontal, distribution: .fill, alignment: .fill, spacing: 0)) let lockImage = UIImageView(image: Asset.commonLock.image.withRenderingMode(.alwaysTemplate)) lockImage.addHeightWidthConstraints(height: 16, width: 16) lockImage.tintColor = Asset.brick.color dataStackView.addArrangedSubview(lockImage) let label3 = UILabel() label3.attributedText = L10n.Cccp.Main.sublabelLockedText.attributed(style: .regular, size: 14, color: Asset.textSnow.color) let view2 = UIView(width: 141, height: 22, color: Asset.pebble.color) view2.layer.cornerRadius = 6 view2.addSubview(label3) UIView.activate(constraints: [ label3.centerXAnchor.constraint(equalTo: view2.centerXAnchor), label3.centerYAnchor.constraint(equalTo: view2.centerYAnchor) ]) dataStackView.addArrangedSubview(view2) dataStackView.addArrangedSubview(UIView(height: 24)) } func update(animated: Bool) { superview?.clipsToBounds = false headerLbl.text = nil subheaderLbl.text = nil dataLbl.text = nil // dataStackView.isHidden = true dataLbl.alpha = 1 dataStackView.arrangedSubviews.forEach({ $0.removeFromSuperview() }) let bill = root ? self.selectedCashBill : nil let header = bill != nil ? L10n.Cccp.Main.bill + " " + bill!.gtoken_out.quantity.symbol : action?.menu?.title ?? lzHeader let subheader = bill?.creationDate.string(pattern: "dd.MM.yyyy HH:mm:ss") ?? action?.menu?.text ?? lzSubheader let data = bill?.token_out.quantity.toNormalizedString(group: true) ?? action?.menu?.details ?? lzData let badge = action?.menu?.badge?.0 ?? self.badge let icon = action?.icon ?? self.icon if style == .default { headerLbl.attributedText = header?.localized.attributed(style: .medium, size: 14, color: Asset.textGranite.color) subheaderLbl.attributedText = subheader?.localized.attributed(style: .regular, size: 12, color: Asset.textGranite.color) subheaderOffset.constant = subheader?.isEmpty == true ? 0 : 2 dataLbl.attributedText = data?.localized.attributed(style: .bold, size: 20, color: Asset.textCoal.color) dataOffset.constant = data?.isEmpty == true ? 0 : 4 } else { headerLbl.attributedText = data?.localized.attributed(style: .bold, size: 20, color: Asset.textCoal.color) subheaderLbl.attributedText = header?.localized.attributed(style: .medium, size: 14, color: Asset.textGranite.color) subheaderOffset.constant = header?.isEmpty == true ? 0 : 2 dataLbl.attributedText = subheader?.localized.attributed(style: .regular, size: 12, color: Asset.textGranite.color) dataOffset.constant = subheader?.isEmpty == true ? 0 : 4 } if let badge = badge?.localized { badgeBtn.isHidden = false badgeBtn.setAttributedTitle(badge.attributed(style: .regular, size: 12, color: Asset.textSnow.color), for: .normal) if let color = action?.menu?.badge?.1 ?? badgeColor { badgeBtn.backgroundColor = color } } else { badgeBtn.isHidden = true } actionBtn.setImage(icon, for: .normal) imgShadowView.isHidden = true if let bill = bill { imgView.image = Asset.cashBill.image } else if let image = action?.menu?.image ?? image { imgView.image = image // uncomment for turn on cash tokens shadow // if (action?.menu?.uuid.contains(Network.servers.current.contract(.cash)) ?? false) // && (action?.menu?.uuid.lowercased().dropFirst(3).starts(with: "cash") ?? false) { // imgShadowView.isHidden = false // } } else if let urls = action?.menu?.imageURLs { downloadImage(urls: urls) } else { imgView.image = nil } if bills.count > 0 && !root { actionBtn.setImage(collapsed ? Asset.cashBillUncollapse.image : Asset.cashBillCollapse.image, for: .normal) if bills.contains(where: { $0.id == selectedCashBill?.id }) { changeBillsState() } if !collapsed && bottomStackView.arrangedSubviews.isEmpty { changeBillsState() } let certCount = Int(data?.replacingOccurrences(of: " ", with: "").amount ?? "0") ?? 0 if bills.count > certCount { generateRichSublabel(certCount: "\(certCount)", lockedCertCount: "\(bills.count - certCount)") dataLbl.alpha = 0 } } imgViewOffset.constant = imgView.image == nil && (action?.menu?.imageURLs ?? []).isEmpty ? 0 : 12 imgViewWidth.constant = imgView.image == nil && (action?.menu?.imageURLs ?? []).isEmpty ? 0 : 40 animated ? UIView.animate(withDuration: Animation.fast) { self.layoutIfNeeded() } : layoutIfNeeded() } private func downloadImage(urls: [URL]) { if urls.isEmpty { indicator.stopAnimating() imgView.image = image } else { indicator.startAnimating() imgView.af.setImage(withURL: urls.first!, placeholderImage: image ?? UIImage(), completion: { guard $0.error != nil else { self.indicator.stopAnimating();return } self.downloadImage(urls: urls.removingFist()) }) } } @IBAction func onAction(_: AnyObject?) { guard isEnabled else { return } if let middleAction = middleAction { middleAction() } else { action?.perform(sender: self) } } func prepareForReuse() { bottomStackView.arrangedSubviews.forEach({ $0.removeFromSuperview() }) } var collapsed: Bool = false func changeBillsState() { if !bills.isEmpty { if bottomStackView.arrangedSubviews.isEmpty { collapsed = false for bill in bills { let card = CommonViewCard() card.lzHeader = L10n.Cccp.Main.bill + " " + bill.gtoken_out.quantity.symbol card.lzSubheader = bill.creationDate.string(pattern: "dd.MM.yyyy HH:mm:ss") card.lzData = bill.token_out.quantity.amount.toDecimal() .toString(max: bill.token_out.quantity.precision) + " \(bill.token_out.quantity.symbol)" card.image = Asset.cashBill.image card.shadowContainer.imgView.isHidden = true if bill.id == selectedCashBill?.id { card.actionBtn.setImage(Asset.commonSelected.image, for: .normal) } if let selectCashBillAction = selectCashBillAction { card.middleAction = { selectCashBillAction(bill) } } let sepearatorStackView = UIStackView( subviews: [UIView(width: 12), UIView(height: 1, color: Asset.fog.color), UIView(width: 12)], axis: .horizontal, distribution: .fill, alignment: .fill, spacing: 0) bottomStackView.addArrangedSubview(sepearatorStackView) bottomStackView.addArrangedSubview(card) } actionBtn.setImage(Asset.cashBillCollapse.image, for: .normal) } else { collapsed = true bottomStackView.arrangedSubviews.forEach({ $0.removeFromSuperview() }) actionBtn.setImage(Asset.cashBillUncollapse.image, for: .normal) } } } @IBAction func onRightAction(_: AnyObject?) { guard isEnabled else { return } if !root { changeBillsState() } if let rightAction = rightAction { rightAction() } else { action?.perform(sender: self) } } }