Untitled
unknown
plain_text
a year ago
39 kB
2
Indexable
Never
import UIKit /// Cell for displaying position, holding and order data /// /// ![ScripListPHOCell](../img/DocumentationSnapshot.ScripListPHOCell.png) public protocol ScripListPHOCellDelegate: AnyObject { func didTapDetailsBottomView(at index: Int) func didTapCancelBottomView(at index: Int) func didTapModifyBottomView(at index: Int) func didTapRetryBottomView(at index: Int) } open class ScripListPHOCell: UITableViewCell { // MARK: - Properties public weak var delegate: ScripListPHOCellDelegate? private let leadingCheckbox: Checkbox = { let checkbox = Checkbox() checkbox.isHidden = true checkbox.isUserInteractionEnabled = false return checkbox }() private let rootStackView: UIStackView = { let stackView = UIStackView() stackView.axis = .vertical return stackView }() private let mainHorizontalStackView: UIStackView = { let stackView = UIStackView() stackView.isLayoutMarginsRelativeArrangement = true stackView.directionalLayoutMargins = Constants.mainHorizontalStackViewDefaultMargins return stackView }() private let bottomView: ScripListPHOView = { let view = ScripListPHOView(size: .small, isTapNeededForLegs: false) view.setContentHuggingPriority(.defaultHigh, for: .vertical) view.isHidden = true return view }() private let failureBottomView: BottomFailureView = { let view = BottomFailureView() view.isHidden = true return view }() private let mainVerticalStackView: UIStackView = { let stackView = UIStackView() stackView.axis = .vertical stackView.spacing = .halfOfQuarterOfDefaultMargin return stackView }() private let leadingPrimaryLabel = ScripName() private let trailingPrimaryLabel: Label = { let label = Label(.bodyBold) label.textAlignment = .right return label }() private let trailingPrimaryImageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .center imageView.isHidden = true return imageView }() private let trailingPrimaryStackView: UIStackView = { let stackView = UIStackView() stackView.spacing = .halfOfThreeQuartersOfDefaultMargin return stackView }() private let primaryStackView = UIStackView() private let leadingSecondaryLabel: Label = { let label = Label(.meta) label.textColor = .appUI5Alt return label }() private let trailingStackViewForTopLabel: UIStackView = { let stackView = UIStackView() stackView.spacing = .halfOfDefaultMargin stackView.alignment = .center return stackView }() private let warningIcon: UIImageView = { let imageView = UIImageView() imageView.image = .appIconErrorCircle16.withColor(.appBackgroundYellow) imageView.contentMode = .scaleAspectFit imageView.setContentHuggingPriority(.required, for: .horizontal) imageView.isHidden = true return imageView }() private let trailingSecondaryLabel: Label = { let label = Label(.meta) label.textColor = .appUI5Alt label.textAlignment = .right return label }() private let secondaryStackView = UIStackView() private let leadingTertiaryLabel: Label = { let label = Label(.meta) label.textColor = .appUI5Alt return label }() private let trailingTertiaryLabel: Label = { let label = Label(.meta) label.textAlignment = .right label.textColor = .appUI5Alt return label }() private let tertiaryStackView = UIStackView() private let bottomButtonsStackView: UIStackView = { let stackView = UIStackView() stackView.backgroundColor = .appUi1 stackView.distribution = .fillEqually stackView.isHidden = true return stackView }() private lazy var detailsBottomView = makeCustomButtonView(image: .appIconFileList2Line, text: L10n.Other.details, shouldAddVerticalSeparator: false) private lazy var modifyBottomView = makeCustomButtonView(image: .appIconEdit2Line, text: L10n.Other.modify) private lazy var cancelBottomView = makeCustomButtonView(image: .appIconCloseCircleLine, text: L10n.Other.cancel) private lazy var retryBottomView = makeCustomButtonView(image: .appIconReloadLine, text: L10n.Other.retry) private let separatorStackView: UIStackView = { let stackView = UIStackView() stackView.isLayoutMarginsRelativeArrangement = true stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: .zero, leading: .defaultMargin, bottom: .zero, trailing: .defaultMargin) return stackView }() private let separator = Divider().withBackgroundColor(.appUi1) private lazy var separatorHeightConstraint = separator.heightAnchor.constraint(equalToConstant: Constants.defaultSeparatorHeight) // MARK: - Lifecycle override public init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) backgroundColor = .appBackgroundDefault selectedBackgroundView = UIView().withBackgroundColor(.appInteractiveUIPressed) rootStackView.addArrangedSubviews( mainHorizontalStackView.addingArrangedSubviews([ leadingCheckbox, mainVerticalStackView.addingArrangedSubviews([ primaryStackView.addingArrangedSubviews([ leadingPrimaryLabel, trailingPrimaryStackView.addingArrangedSubviews([ trailingStackViewForTopLabel.addingArrangedSubviews([trailingPrimaryLabel, warningIcon]), trailingPrimaryImageView ]) ]), secondaryStackView.addingArrangedSubviews([ leadingSecondaryLabel, trailingSecondaryLabel ]), tertiaryStackView.addingArrangedSubviews([ leadingTertiaryLabel, trailingTertiaryLabel ]), bottomView, failureBottomView ]) ]), separatorStackView.addingArrangedSubviews(separator), bottomButtonsStackView.addingArrangedSubviews(detailsBottomView, modifyBottomView, cancelBottomView, retryBottomView) ) contentView.addSubviewsForAutoLayout([rootStackView]) NSLayoutConstraint.activate([ leadingCheckbox.widthAnchor.constraint(equalToConstant: .threeAndHalfOfDefaultMargin), separatorHeightConstraint, rootStackView.topAnchor.constraint(equalTo: contentView.topAnchor), rootStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), rootStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), rootStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), trailingPrimaryImageView.widthAnchor.constraint(equalToConstant: .defaultMargin) ]) detailsBottomView.addGestureRecognizer((UITapGestureRecognizer(target: self, action: #selector(didTapDetailsBottomView(_:))))) cancelBottomView.addGestureRecognizer((UITapGestureRecognizer(target: self, action: #selector(didTapCancelBottomView(_:))))) modifyBottomView.addGestureRecognizer((UITapGestureRecognizer(target: self, action: #selector(didTapModifyBottomView(_:))))) retryBottomView.addGestureRecognizer((UITapGestureRecognizer(target: self, action: #selector(didTapRetryBottomView(_:))))) } @available(*, unavailable) public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override open func prepareForReuse() { super.prepareForReuse() failureBottomView.isHidden = true trailingPrimaryImageView.isHidden = true warningIcon.isHidden = true bottomView.layoutIfNeeded() } override open func setEditing(_ editing: Bool, animated: Bool) { // We need hide the default leading accessory, so will send editing as false here super.setEditing(false, animated: animated) } override open func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) leadingCheckbox.setIsSelected(selected) } public func isHiddenSeparator(_ value: Bool) { separator.isHidden = value } public func configure(name: String = "", PHO: PHO) { switch PHO { case let .orders(orderCategory): updateForOrder(orderCategory) case let .holdings(data): updateForHolding(data: data) case let .positions(data): updateForPosition(data: data) case let .basketOrder(data): updateForBasketItem(data: data) } } public func updateConditionalOrderLTP(with attributedText: NSAttributedString?) { trailingPrimaryLabel.isHidden = attributedText == nil trailingPrimaryLabel.attributedText = attributedText } public func updateBasketOrder(withLTP ltp: String, isBasketOrderExecuted: Bool, side: OrderSide) { if isBasketOrderExecuted { let trailingSecondaryAttributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: L10n.Lists.ltpSuffix(ltp), attributes: [.foregroundColor: UIColor.appUI5Alt])) trailingSecondaryAttributedText.append(NSAttributedString(string: " \(L10n.Lists.interpunct) ")) let sideText = side == .buy ? L10n.Lists.buy : L10n.Lists.sell let sideTextColor = side == .buy ? UIColor.appTextPositiveTrend : UIColor.appTextNegativeTrend let sideAttributedText = sideText.attributedString(textStyle: .meta, originalColor: sideTextColor, subStrings: (sideText, .appText2)) trailingSecondaryAttributedText.append(sideAttributedText) trailingSecondaryLabel.attributedText = trailingSecondaryAttributedText } else { trailingSecondaryLabel.text = L10n.Lists.ltpSuffix(ltp) } } public func updateConditionalOrderPNL(with pnlText: String?, pnlColor: UIColor) { bottomView.updatePNL(pnlText, pnlColor: pnlColor) } public func updateWarningIconVisibility(to shouldShow: Bool) { warningIcon.isHidden = !shouldShow } } // MARK: - UpdateUIHelper private typealias UpdateUIHelper = ScripListPHOCell private extension UpdateUIHelper { func setDefaultSeparator() { separatorStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: .zero, leading: .defaultMargin, bottom: .zero, trailing: .defaultMargin) separatorHeightConstraint.constant = Constants.defaultSeparatorHeight layoutIfNeeded() } func setConditionalSeparator() { separatorStackView.directionalLayoutMargins = .zero separatorHeightConstraint.constant = Constants.conditionalSeparatorHeight layoutIfNeeded() } func updateForOrder(_ order: PHO.OrderCategory) { switch order { case .regular(let (order, filling, trailingImage)): trailingPrimaryLabel.isHidden = false leadingSecondaryLabel.isHidden = false trailingSecondaryLabel.isHidden = false leadingTertiaryLabel.isHidden = false trailingTertiaryLabel.isHidden = false tertiaryStackView.isHidden = false failureBottomView.isHidden = true bottomView.isHidden = true trailingPrimaryImageView.isHidden = true backgroundColor = order.status == .failed ? .appDataNegativeSubtle : backgroundColor leadingPrimaryLabel.configure(.scripName(title: order.name, tag: nil, titleFont: .bodyBold, trailingImage: trailingImage)) leadingSecondaryLabel.text = additionalScripInfo(index: order.index, instrumentType: order.instrumentType, date: order.date) let productTypeAttributedText = NSAttributedString(string: "\(order.productType.title) ", attributes: [.foregroundColor: UIColor.appText1]) leadingTertiaryLabel.attributedText = productTypeAttributedText trailingPrimaryLabel.textColor = order.status == .failed ? .appSupportError : .appText1 trailingPrimaryLabel.text = !order.isScheduled ? titleForOrderStatus(order.status) : L10n.Lists.scheduled switch order.side { case .buy: trailingSecondaryLabel.textColor = .appTextPositiveTrend trailingSecondaryLabel.text = L10n.Lists.buy case .sell: trailingSecondaryLabel.textColor = .appTextNegativeTrend trailingSecondaryLabel.text = L10n.Lists.sell } let price: String = { if case let .limit(limit) = filling { return order.isCurrency ? FormattedNumber.currencyFourFractionals(limit).stringValue : FormattedNumber.currency(limit).stringValue } else { return L10n.Lists.market } }() trailingTertiaryLabel.textColor = .appText2 trailingTertiaryLabel.text = "\(order.quantityFilled)/\(order.quantityOrdered) \(L10n.Other.qty)." + " @ " + price separator.backgroundColor = order.isOpen ? .appUi1 : .appUi3 setDefaultSeparator() if order.status == .failed { trailingPrimaryImageView.isHidden = false trailingPrimaryImageView.image = .appIconErrorCircle16 trailingPrimaryImageView.tintColor = .appSupportError if !order.message.isEmpty { failureBottomView.isHidden = false failureBottomView.configure(with: .regularError(order.message)) } } warningIcon.isHidden = !order.shouldShowWarningIcon case .conditional(let(parent, childLegs, scripDetails, message)): warningIcon.isHidden = true bottomView.isHidden = false trailingPrimaryLabel.isHidden = false leadingSecondaryLabel.isHidden = false trailingSecondaryLabel.isHidden = false leadingTertiaryLabel.isHidden = true trailingTertiaryLabel.isHidden = true tertiaryStackView.isHidden = true failureBottomView.isHidden = true trailingPrimaryImageView.isHidden = true trailingPrimaryLabel.attributedText = nil leadingPrimaryLabel.configure(.scripName(title: scripDetails.scripName, tag: nil, titleFont: .bodyBold)) leadingSecondaryLabel.text = additionalScripInfo(index: scripDetails.scripIndex, instrumentType: scripDetails.instrumentType, date: scripDetails.scripDate) trailingSecondaryLabel.text = scripDetails.scripProductType.title trailingSecondaryLabel.textColor = .appUi5 trailingPrimaryLabel.textColor = .appText2 updateConditionalOrderLTP(with: Self.getAttributedLTP(ltpValue: parent.ltp, ltpColor: parent.ltpColor)) updateConditionalOrderPNL(with: parent.pnlValue, pnlColor: parent.pnlColor) setConditionalSeparator() separator.backgroundColor = .appUi3 if let errorMessage = message, !errorMessage.isEmpty { failureBottomView.isHidden = false failureBottomView.configure(with: .conditionalError(errorMessage)) } bottomView.configureParentStatusView(parent: parent) bottomView.configureConditionalChildsViews(childs: childLegs, parentInfo: parent) } } func updateForHolding(data: HoldingDataModel) { warningIcon.isHidden = true leadingPrimaryLabel.configure(.scripName(title: data.name, tag: nil, titleFont: .bodyBold)) updateOverall(overall: data.overall, percentage: data.percentageChange, showFourFractionalsForPrice: data.showFourFractionalsForPrice) leadingSecondaryLabel.text = L10n.Lists.invested(FormattedNumber.currency(data.amountInvested).stringValue) updateAveragePrice(price: data.averagePrice, showFourFractionalsForPrice: data.showFourFractionalsForPrice) let quantityString = NSMutableAttributedString() let quantity = NSAttributedString( string: L10n.Lists.quantityPrefix(data.quantity), attributes: [.foregroundColor: UIColor.appUI5Alt] ) quantityString.append(quantity) quantityString.append(getAdditionalQuantityInfo(additionalInfo: data.quantityAdditionalInfo)) leadingTertiaryLabel.attributedText = quantityString if !data.isIlliquid { updateTextForLTP(ltp: data.ltp, ltpPercentage: data.ltpPercentage, showFourFractionalsForPrice: data.showFourFractionalsForPrice) } else { trailingTertiaryLabel.text = L10n.UIElements.illiquid } separator.backgroundColor = data.isActive ? .appUi1 : .appUi3 } func updateForPosition(data: PositionDataModel) { warningIcon.isHidden = true leadingPrimaryLabel.configure(.scripName(title: data.name, tag: nil, titleFont: .bodyBold)) updateOverall(overall: data.overall, showFourFractionalsForPrice: false) leadingSecondaryLabel.text = additionalScripInfo(index: data.index, instrumentType: data.instrumentType, date: data.date) updateAveragePrice(price: data.averagePriceOfShare, showFourFractionalsForPrice: data.showFourFractionalsForPrice) updateQuantity(productType: data.productType.title, quantity: data.quantityOfShares) updateTextForLTP(ltp: data.lastTradedPrice, ltpPercentage: data.ltpPercentage, showFourFractionalsForPrice: data.showFourFractionalsForPrice) separator.backgroundColor = data.isOpen ? .appUi1 : .appUi3 bottomView.isHidden = true } func updateForBasketItem(data: BasketOrderDataModel) { mainHorizontalStackView.directionalLayoutMargins = data.isEditing ? NSDirectionalEdgeInsets(top: .halfOfDefaultMargin, leading: .zero, bottom: .halfOfDefaultMargin, trailing: .defaultMargin) : Constants.mainHorizontalStackViewDefaultMargins leadingCheckbox.isHidden = !data.isEditing trailingPrimaryLabel.textAlignment = data.isEditing ? .left : .right mainVerticalStackView.spacing = data.isEditing ? .halfOfThreeQuartersOfDefaultMargin : .halfOfQuarterOfDefaultMargin trailingSecondaryLabel.isHidden = data.isEditing tertiaryStackView.isHidden = data.isEditing leadingPrimaryLabel.isTitleExpanded = data.expansionDetails.isExpanded leadingPrimaryLabel.configure(.scripName(title: data.name, shouldAddSeparatorAfterTitle: data.isEditing, tag: nil, titleFont: .bodyBold, titleColor: data.isExecuted ? .appTextLink : .appText1, isExpandable: data.isExecuted, shouldShowTagDate: !data.isEditing)) leadingSecondaryLabel.text = additionalScripInfo(index: data.index, instrumentType: data.segment, date: nil) let productTypeAttributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: "\(data.productType.title)", attributes: [.foregroundColor: UIColor.appText1])) productTypeAttributedText.append(NSAttributedString(string: " \(L10n.Lists.interpunct) ")) let quantityAttributedText = data.orderType.attributedString(textStyle: .meta, originalColor: .appUI5Alt, subStrings: (data.orderType, .appText2)) productTypeAttributedText.append(quantityAttributedText) leadingTertiaryLabel.attributedText = productTypeAttributedText if data.isExecuted { trailingPrimaryLabel.textColor = data.orderStatus.color trailingPrimaryLabel.text = !data.isScheduled ? titleForOrderStatus(data.orderStatus) : L10n.Lists.scheduled } else { switch data.side { case .buy: trailingPrimaryLabel.textColor = .appTextPositiveTrend trailingPrimaryLabel.text = L10n.Lists.buy case .sell: trailingPrimaryLabel.textColor = .appTextNegativeTrend trailingPrimaryLabel.text = L10n.Lists.sell } } trailingSecondaryLabel.textColor = .appUi5 trailingTertiaryLabel.textColor = .appText2 let priceString = (data.shouldShowFourFractionalsForPrice ? FormattedNumber.currencyFourFractionals(data.price) : FormattedNumber.currency(data.price)).stringValue let quantityText = data.isLimitPrice ? "\(data.quantity) \(L10n.Other.qty). @ \(priceString)" : "\(data.quantity) \(L10n.Other.qty)." trailingTertiaryLabel.text = quantityText bottomButtonsStackView.isHidden = !data.expansionDetails.isExpanded guard data.isExecuted else { return } detailsBottomView.isHidden = false switch data.expansionDetails.buttonsConfig { case .detailsModifyCancel: modifyBottomView.isHidden = false cancelBottomView.isHidden = false retryBottomView.isHidden = true case .detailsRetry: retryBottomView.isHidden = false modifyBottomView.isHidden = true cancelBottomView.isHidden = true case .details: modifyBottomView.isHidden = true cancelBottomView.isHidden = true retryBottomView.isHidden = true } } func titleForOrderStatus(_ orderStatus: OrderStatus) -> String { switch orderStatus { case .triggerPending: return L10n.Lists.triggerPendingOrders default: return orderStatus.title } } func additionalScripInfo(index: String, instrumentType: String?, date: Date?) -> String { var string = "\(index)" if let date = date { string.append(" " + L10n.Lists.interpunct + " ") string.append("\(DateFormatter.scripInlineSpacedFormatter.string(from: date).uppercased())") } if let instrumentType = instrumentType { string.append(" \(instrumentType)") } return string } func getAdditionalQuantityInfo(additionalInfo: OrderQuantityAdditionalInformation?) -> NSAttributedString { guard let additionalInfo = additionalInfo else { return NSAttributedString(string: "") } let attributedString: NSAttributedString switch additionalInfo { case let .t1(quantity): attributedString = NSAttributedString(string: L10n.Lists.t1PendingText(quantity), attributes: [.foregroundColor: UIColor.appSupportProcessing]) case .delisted: attributedString = NSAttributedString(string: L10n.Lists.delistedSuffix, attributes: [.foregroundColor: UIColor.appSupportWarning]) case .suspended: attributedString = NSAttributedString(string: L10n.Lists.suspendedSuffix, attributes: [.foregroundColor: UIColor.appSupportWarning]) } return attributedString } func updateQuantity(productType: String, quantity: Int) { let attributedText = NSMutableAttributedString() let productTypeAttributedText = NSAttributedString(string: "\(productType) ", attributes: [.foregroundColor: UIColor.appText2]) attributedText.append(productTypeAttributedText) let seperatorAttachment = NSTextAttachment() seperatorAttachment.image = .appIconSeperator attributedText.append(NSAttributedString(attachment: seperatorAttachment)) attributedText.append(NSAttributedString(string: " ")) let quantityInDouble = Double(quantity) let quantityFormattedText = FormattedNumber.decimalNoFractionalWithSign(quantityInDouble).stringValue let quantityTextColor = quantityInDouble.currencyColor(defaultColor: .appText2) let quantityAttributedText = L10n.Lists.quantityPrefix(quantityFormattedText).attributedString(textStyle: .meta, originalColor: .appUI5Alt, subStrings: (quantityFormattedText, quantityTextColor)) attributedText.append(quantityAttributedText) leadingTertiaryLabel.attributedText = attributedText } func updateOverall(overall: Double?, percentage: Double? = nil, showFourFractionalsForPrice: Bool) { let overallText = showFourFractionalsForPrice ? FormattedNumber.currencyWithSignFourFractionals(overall).stringValue : FormattedNumber.currencyWithSign(overall).stringValue let overallTextColor = overall?.currencyColor(defaultColor: .appText2) ?? .appText2 guard let percentage = percentage else { trailingPrimaryLabel.textColor = overallTextColor trailingPrimaryLabel.text = overallText return } let percentChangeText = FormattedNumber.percentWithSign(percentage).stringValue trailingPrimaryLabel.attributedText = "\(overallText) (\(percentChangeText))".attributedString(textStyle: .meta, originalColor: overallTextColor, subStrings: (overallText, overallTextColor, .bodyBold)) } func updateAveragePrice(price: Double?, showFourFractionalsForPrice: Bool) { let avgPriceFormatter: FormattedNumber = showFourFractionalsForPrice ? FormattedNumber.currencyFourFractionals(price) : FormattedNumber.currency(price) trailingSecondaryLabel.text = L10n.Lists.averageSuffix(avgPriceFormatter.stringValue) } func updateTextForLTP(ltp: Double?, ltpPercentage: Double?, showFourFractionalsForPrice: Bool) { let formattedLTPText = showFourFractionalsForPrice ? FormattedNumber.currencyFourFractionals(ltp).stringValue : FormattedNumber.currency(ltp).stringValue let formattedLTPChangeText = "(\(FormattedNumber.percentWithSignWithMultiplier(ltpPercentage).stringValue))" let trailingTertiaryLabelText = L10n.Lists.ltpSuffix("\(formattedLTPText) \(formattedLTPChangeText)") let formattedLTPChangeTextColor = ltpPercentage?.currencyColor(defaultColor: .appText2) ?? .appText2 trailingTertiaryLabel.attributedText = trailingTertiaryLabelText.attributedString(textStyle: .meta, originalColor: .appUI5Alt, subStrings: (formattedLTPChangeText, formattedLTPChangeTextColor)) } } public extension ScripListPHOCell { public static func getAttributedLTP(ltpValue: String?, ltpColor: UIColor) -> NSAttributedString? { guard let ltpValue = ltpValue else { return nil } let attributedText: NSAttributedString = L10n.Lists.ltp(ltpValue).attributedString(textStyle: .meta, originalColor: .appUI5Alt, subStrings: (ltpValue, ltpColor, .metaBold)) return attributedText } } // MARK: Substructures private typealias Substructures = ScripListPHOCell private extension Substructures { public struct HoldingDataModel { let name: String let amountInvested: Double? let overall: Double? let percentageChange: Double? let quantity: Int let quantityAdditionalInfo: OrderQuantityAdditionalInformation? let averagePrice: Double? let showFourFractionalsForPrice: Bool let ltp: Double? let ltpPercentage: Double? let isActive: Bool let isIlliquid: Bool public init(name: String, amountInvested: Double?, overall: Double?, percentageChange: Double?, quantity: Int, quantityAdditionalInfo: OrderQuantityAdditionalInformation?, averagePrice: Double?, showFourFractionalsForPrice: Bool, ltp: Double?, ltpPercentage: Double?, isActive: Bool, isIlliquid: Bool) { self.name = name self.amountInvested = amountInvested self.overall = overall self.percentageChange = percentageChange self.quantity = quantity self.quantityAdditionalInfo = quantityAdditionalInfo self.averagePrice = averagePrice self.showFourFractionalsForPrice = showFourFractionalsForPrice self.ltp = ltp self.ltpPercentage = ltpPercentage self.isActive = isActive self.isIlliquid = isIlliquid } } public struct BasketOrderDataModel { public struct ExpansionDetails { public enum ButtonsConfig { case detailsModifyCancel case detailsRetry case details } let isExpanded: Bool let buttonsConfig: ButtonsConfig public init(isExpanded: Bool, buttonsConfig: ButtonsConfig) { self.isExpanded = isExpanded self.buttonsConfig = buttonsConfig } } let name: String let side: OrderSide let productType: ProductType let quantity: Int let orderType: String let price: Double let index: String let segment: String let isLimitPrice: Bool let isEditing: Bool let isExecuted: Bool let expansionDetails: ExpansionDetails let orderStatus: OrderStatus let isScheduled: Bool let shouldShowFourFractionalsForPrice: Bool public init(name: String, side: OrderSide, productType: ProductType, quantity: Int, orderType: String, price: Double, index: String, segment: String, isLimitPrice: Bool, isEditing: Bool, isExecuted: Bool, expansionDetails: ExpansionDetails, orderStatus: OrderStatus, isScheduled: Bool, shouldShowFourFractionalsForPrice: Bool) { self.name = name self.side = side self.productType = productType self.quantity = quantity self.orderType = orderType self.price = price self.index = index self.segment = segment self.isLimitPrice = isLimitPrice self.isEditing = isEditing self.isExecuted = isExecuted self.expansionDetails = expansionDetails self.orderStatus = orderStatus self.isScheduled = isScheduled self.shouldShowFourFractionalsForPrice = shouldShowFourFractionalsForPrice } } public struct PositionDataModel { let name: String let index: String let date: Date? let productType: ProductType let overall: Double? let lastTradedPrice: Double? let quantityOfShares: Int let averagePriceOfShare: Double let showFourFractionalsForPrice: Bool let ltpPercentage: Double let isOpen: Bool let instrumentType: String? public init(name: String, index: String, date: Date?, productType: ProductType, overall: Double?, lastTradedPrice: Double?, quantityOfShares: Int, averagePriceOfShare: Double, showFourFractionalsForPrice: Bool = false, ltpPercentage: Double, isOpen: Bool, instrumentType: String? = nil) { self.name = name self.index = index self.date = date self.productType = productType self.overall = overall self.lastTradedPrice = lastTradedPrice self.quantityOfShares = quantityOfShares self.averagePriceOfShare = averagePriceOfShare self.showFourFractionalsForPrice = showFourFractionalsForPrice self.ltpPercentage = ltpPercentage self.isOpen = isOpen self.instrumentType = instrumentType } } public enum PHO { case positions(PositionDataModel) case holdings(HoldingDataModel) case orders(OrderCategory) case basketOrder(BasketOrderDataModel) public enum OrderCategory { case regular(order: Order, filling: OrderFilling, trailingImage: UIImage? = nil) case conditional( parent: ScripListPHOView.ParentViewInfo, childLegs: [ScripListPHOView.ConditionalViewInfo], scripDetails: ScripDetails, errorMessage: String? ) } } public enum OrderSide { case buy case sell var title: String { switch self { case .buy: return L10n.Lists.buy case .sell: return L10n.Lists.sell } } } public struct ScripDetails { let scripName: String let scripIndex: String let scripDate: Date? let scripProductType: ProductType let scripQuantity: Int let scripSegment: String? let instrumentType: String? public init(scripName: String, scripIndex: String, scripDate: Date?, scripProductType: ProductType, scripQuantity: Int, scripSegment: String?, instrumentType: String?) { self.scripName = scripName self.scripIndex = scripIndex self.scripDate = scripDate self.scripProductType = scripProductType self.scripQuantity = scripQuantity self.scripSegment = scripSegment self.instrumentType = instrumentType } } public struct Order { let name: String let index: String let date: Date? let productType: ProductType let status: OrderStatus let side: OrderSide let quantityFilled: Int let quantityOrdered: Int let isScheduled: Bool let isCurrency: Bool let price: OrderPrice let instrumentType: String? let isOpen: Bool let message: String let shouldShowWarningIcon: Bool public init(name: String, index: String, date: Date?, productType: ProductType, status: OrderStatus, side: OrderSide, quantityFilled: Int, quantityOrdered: Int, isScheduled: Bool, isCurrency: Bool, price: OrderPrice, instrumentType: String?, isOpen: Bool, message: String, shouldShowWarningIcon: Bool) { self.name = name self.index = index self.date = date self.productType = productType self.status = status self.side = side self.quantityFilled = quantityFilled self.quantityOrdered = quantityOrdered self.isScheduled = isScheduled self.isCurrency = isCurrency self.price = price self.instrumentType = instrumentType self.isOpen = isOpen self.message = message self.shouldShowWarningIcon = shouldShowWarningIcon } } public struct OrderPrice { let stopLoss: Double? let squareOff: Double? public init(stopLoss: Double?, squareOff: Double?) { self.stopLoss = stopLoss self.squareOff = squareOff } } public struct OrderLeg { public let title: String public let subtitle: String public let status: OrderStatus public let completionHandler: (() -> Void)? public init(title: String, subtitle: String, status: OrderStatus, completionHandler: (() -> Void)?) { self.title = title self.status = status self.completionHandler = completionHandler self.subtitle = subtitle } } public enum OrderPriceBand { case above case below var title: String { switch self { case .above: return L10n.Other.above case .below: return L10n.Other.below } } } public enum OrderFilling { case limit(Double) case market case smartOrder } public enum OrderQuantityAdditionalInformation { case suspended case delisted case t1(Int) } public enum ProductType: Hashable { case intraday case delivery case mtf case mtfs case co case oco case strategy var title: String { switch self { case .intraday: return L10n.Lists.intraday case .delivery: return L10n.Lists.delivery case .mtf: return L10n.Lists.mtf case .mtfs: return L10n.Lists.mtfs case .co: return L10n.Lists.co case .oco: return L10n.Lists.oco case .strategy: return L10n.Lists.oco } } } public enum BasketOrderStatus { case completed case error case pending case inQueue case cancelled } } // MARK: Helper private typealias Helper = ScripListPHOCell private extension Helper { func makeCustomButtonView(image: UIImage, text: String, shouldAddVerticalSeparator: Bool = true) -> UIView { let label = Label(.metaBold) label.textAlignment = .center let attachment = NSTextAttachment() attachment.bounds = CGRect(x: .zero, y: -.quarterOfDefaultMargin, width: .defaultMargin, height: .defaultMargin) attachment.image = image let space = NSTextAttachment() space.bounds.size.width = .halfOfDefaultMargin let attributedText = NSMutableAttributedString() attributedText.append(NSAttributedString(attachment: attachment)) attributedText.append(NSAttributedString(attachment: space)) attributedText.append(NSAttributedString(string: text)) label.attributedText = attributedText let contentStackView = UIStackView() contentStackView.spacing = .halfOfDefaultMargin contentStackView.addArrangedSubview(label) let mainStackView = UIStackView() mainStackView.isLayoutMarginsRelativeArrangement = true mainStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: .halfOfDefaultMargin, leading: .zero, bottom: .halfOfDefaultMargin, trailing: .zero) if shouldAddVerticalSeparator { let verticalDivider = UIView().withBackgroundColor(.appUi3) verticalDivider.widthAnchor.constraint(equalToConstant: 1).isActive = true mainStackView.addArrangedSubview(verticalDivider) } mainStackView.addArrangedSubview(contentStackView) mainStackView.isHidden = true return mainStackView } } // MARK: ActionsHelper private typealias ActionsHelper = ScripListPHOCell private extension ActionsHelper { @objc func didTapDetailsBottomView(_ gestureRecognizer: UITapGestureRecognizer) { delegate?.didTapDetailsBottomView(at: tag) } @objc func didTapCancelBottomView(_ gestureRecognizer: UITapGestureRecognizer) { delegate?.didTapCancelBottomView(at: tag) } @objc func didTapModifyBottomView(_ gestureRecognizer: UITapGestureRecognizer) { delegate?.didTapModifyBottomView(at: tag) } @objc func didTapRetryBottomView(_ gestureRecognizer: UITapGestureRecognizer) { delegate?.didTapRetryBottomView(at: tag) } } // MARK: Constants private typealias Constant = ScripListPHOCell private extension Constant { enum Constants { static let conditionalSeparatorHeight: CGFloat = 4 static let defaultSeparatorHeight: CGFloat = 1 static let mainHorizontalStackViewDefaultMargins = NSDirectionalEdgeInsets(top: .threeQuartersOfDefaultMargin, leading: .defaultMargin, bottom: .threeQuartersOfDefaultMargin, trailing: .defaultMargin) } }