// // LNCreateOrderFromSkillListPanel.swift // Lanu // // Created by OneeChan on 2025/12/2. // import Foundation import UIKit import SnapKit class LNCreateOrderFromSkillListPanel: LNPopupView { private let skillListView = UIStackView() private let costLabel = UILabel() private let minusButton = UIButton() private let countLabel = UILabel() private let addButton = UIButton() private var curCount = 1 { didSet { countLabel.text = "\(curCount)" if curCount <= 1 { minusButton.isEnabled = false } else { minusButton.isEnabled = true } updatePrice() } } private var skillItemViews: [LNProfileOrderSkillItemView] = [] private var curSelected: LNGameMateSkillVO? { didSet { skillItemViews.forEach { $0.isSelected = $0.cur?.id == curSelected?.id } updatePrice() } } override init(frame: CGRect) { super.init(frame: frame) setupViews() } func update(_ skills: [LNGameMateSkillVO], selected: LNGameMateSkillVO?) { guard !skills.isEmpty else { return } skillListView.arrangedSubviews.forEach { skillListView.removeArrangedSubview($0) $0.removeFromSuperview() } skills.forEach { skill in let view = LNProfileOrderSkillItemView() view.update(skill) view.onTap { [weak self] in guard let self else { return } self.curSelected = skill } skillListView.addArrangedSubview(view) skillItemViews.append(view) } curSelected = selected ?? skills[0] curCount = 1 } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNCreateOrderFromSkillListPanel { private func updatePrice() { guard let skill = curSelected else { return } let cost = skill.price * Double(curCount) if let discount = LNOrderManager.shared.discountFor(skill.price) { costLabel.text = (cost - (1 - discount) * skill.price).toDisplay } else { costLabel.text = cost.toDisplay } } private func setupViews() { let titleLabel = UILabel() titleLabel.font = .heading_h3 titleLabel.textColor = .text_5 titleLabel.textAlignment = .center titleLabel.text = .init(key: "A00291") container.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.top.equalToSuperview().offset(28) } skillListView.axis = .vertical skillListView.spacing = 8 container.addSubview(skillListView) skillListView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.top.equalTo(titleLabel.snp.bottom).offset(20) } let priceView = buildPriceView() container.addSubview(priceView) priceView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalTo(skillListView.snp.bottom).offset(4) } let orderView = buildOrderButton() container.addSubview(orderView) orderView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalTo(priceView.snp.bottom).offset(17) make.bottom.equalTo(commonBottomInset) } } private func buildPriceView() -> UIView { let container = UIView() container.snp.makeConstraints { make in make.height.equalTo(54) } let label = UILabel() label.text = .init(key: "A00122") label.font = .heading_h3 label.textColor = .text_5 container.addSubview(label) label.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalToSuperview().offset(20) } addButton.setTitle("+", for: .normal) addButton.setTitleColor(.text_4, for: .normal) addButton.setTitleColor(.text_2, for: .disabled) addButton.backgroundColor = .primary_1 addButton.layer.cornerRadius = 12 addButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } self.curCount += 1 }), for: .touchUpInside) container.addSubview(addButton) addButton.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-20) make.width.height.equalTo(24) } countLabel.font = .body_m countLabel.textColor = .text_5 container.addSubview(countLabel) countLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalTo(addButton.snp.leading).offset(-10) } minusButton.setTitle("-", for: .normal) minusButton.setTitleColor(.text_4, for: .normal) minusButton.setTitleColor(.text_2, for: .disabled) minusButton.backgroundColor = .primary_1 minusButton.layer.cornerRadius = 12 minusButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } self.curCount -= 1 }), for: .touchUpInside) container.addSubview(minusButton) minusButton.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalTo(countLabel.snp.leading).offset(-10) make.width.height.equalTo(24) } return container } private func buildOrderButton() -> UIView { let container = UIView() let buttonView = UIView() buttonView.backgroundColor = .init(hex: "#1789FF14") buttonView.layer.cornerRadius = 23.5 container.addSubview(buttonView) buttonView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.verticalEdges.equalToSuperview() make.height.equalTo(47) } let orderButton = UIButton() orderButton.setBackgroundImage(.icSkillOrderLong, for: .normal) orderButton.setTitle(.init(key: "A00041"), for: .normal) orderButton.setTitleColor(.text_1, for: .normal) orderButton.titleLabel?.font = .heading_h3 orderButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let curSelected else { return } dismiss() showLoading() LNGameMateManager.shared.getSkillDetail(skillId: curSelected.id) { [weak self] detail in dismissLoading() guard let self else { return } guard let detail else { return } pushToCreateOrder(detail, count: curCount) } }), for: .touchUpInside) buttonView.addSubview(orderButton) orderButton.snp.makeConstraints { make in make.trailing.equalToSuperview() make.verticalEdges.equalToSuperview() } let descView = UIView() buttonView.addSubview(descView) descView.snp.makeConstraints { make in make.leading.verticalEdges.equalToSuperview() make.trailing.equalTo(orderButton.snp.leading) } let priceView = UIView() descView.addSubview(priceView) priceView.snp.makeConstraints { make in make.center.equalToSuperview() } let coin = UIImageView.coinImageView() priceView.addSubview(coin) coin.snp.makeConstraints { make in make.leading.centerY.equalToSuperview() make.width.height.equalTo(20) } costLabel.font = .heading_h2 costLabel.textColor = .text_5 priceView.addSubview(costLabel) costLabel.snp.makeConstraints { make in make.verticalEdges.trailing.equalToSuperview() make.leading.equalTo(coin.snp.trailing).offset(4) } return container } } private class LNProfileOrderSkillItemView: UIView { private let icon = UIImageView() private let nameLabel = UILabel() private let priceLabel = UILabel() private let discountView = LNNewbieDiscountView() var cur: LNGameMateSkillVO? var isSelected: Bool = false { didSet { if isSelected { layer.borderColor = UIColor.init(hex: "#1789FF").cgColor backgroundColor = .fill_5 } else { layer.borderColor = UIColor.fill_4.cgColor backgroundColor = .clear } } } func update(_ skill: LNGameMateSkillVO) { icon.sd_setImage(with: URL(string: skill.icon)) nameLabel.text = skill.name let text: String = .init(key: "A00040", skill.price.toDisplay, skill.unit) let attrStr = NSMutableAttributedString(string: text) let range = (text as NSString).range(of: "\(skill.price.toDisplay)") attrStr.addAttribute(.font, value: UIFont.heading_h4, range: range) priceLabel.attributedText = attrStr if let discount = LNOrderManager.shared.discountFor(skill.price) { discountView.isHidden = false discountView.update(1 - discount) } else { discountView.isHidden = true } cur = skill } override init(frame: CGRect) { super.init(frame: frame) layer.cornerRadius = 12 layer.borderWidth = 1 layer.borderColor = UIColor.fill_4.cgColor snp.makeConstraints { make in make.height.equalTo(52) } addSubview(icon) icon.snp.makeConstraints { make in make.leading.equalToSuperview().offset(14) make.centerY.equalToSuperview() make.width.height.equalTo(32) } priceLabel.font = .body_s priceLabel.textColor = .text_4 priceLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) priceLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) addSubview(priceLabel) priceLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-15) } discountView.discountOnly = true addSubview(discountView) discountView.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalTo(priceLabel.snp.leading).offset(-8) } nameLabel.font = .heading_h4 nameLabel.textColor = .text_5 nameLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) addSubview(nameLabel) nameLabel.snp.makeConstraints { make in make.leading.equalTo(icon.snp.trailing).offset(10) make.centerY.equalToSuperview() make.trailing.lessThanOrEqualTo(discountView.snp.leading).offset(-8) } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } #if DEBUG import SwiftUI struct LNProfileOrderPanelPreview: UIViewRepresentable { func makeUIView(context: Context) -> some UIView { let container = UIView() container.backgroundColor = .lightGray let view = LNCreateOrderFromSkillListPanel() view.popup(container) return container } func updateUIView(_ uiView: UIViewType, context: Context) { } } #Preview(body: { LNProfileOrderPanelPreview() }) #endif // DEBUG