| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- //
- // LNCreateOrderViewController.swift
- // Lanu
- //
- // Created by OneeChan on 2025/12/22.
- //
- import Foundation
- import UIKit
- import SnapKit
- extension UIView {
- func pushToCreateOrder(_ skillDetail: LNGameMateSkillDetailVO) {
- let vc = LNCreateOrderViewController()
- vc.loadSkill(skillDetail)
- navigationController?.pushViewController(vc, animated: true)
- }
-
- func pushToCreateOrder(_ qrCode: LNQRCodeDetailResponse) {
- let vc = LNCreateOrderViewController()
- vc.loadQRData(qrCode)
- navigationController?.pushViewController(vc, animated: true)
- }
- }
- class LNCreateOrderViewController: LNViewController {
- private var skill: LNGameMateSkillDetailVO?
- private var qrDetail: LNQRCodeDetailResponse?
-
- private let avatar = UIImageView()
- private let nameLabel = UILabel()
- private let starLabel = UILabel()
-
- private let skillIc = UIImageView()
- private let skillNameLabel = UILabel()
- private let skillPriceLabel = UILabel()
-
- private let countLabel = UILabel()
- private let minuButton = UIButton()
- private let addButton = UIButton()
- private var curCount = 1 {
- didSet {
- countLabel.text = "\(curCount)"
- if curCount <= 1 {
- minuButton.isEnabled = false
- } else {
- minuButton.isEnabled = true
- }
- updateCost()
- }
- }
-
- private let extraInput = UITextField()
-
- private let costLabel = UILabel()
- private let orderButton = UIButton()
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- setupViews()
- }
-
- func loadSkill(_ detail: LNGameMateSkillDetailVO) {
- skill = detail
- updateContent(detail)
- }
-
- func loadQRData(_ data: LNQRCodeDetailResponse) {
- qrDetail = data
- updateContent(data)
- }
- }
- extension LNCreateOrderViewController: UITextFieldDelegate {
- func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
- let currentText = textField.text ?? ""
-
- guard let range = Range(range, in: currentText) else { return false }
- let newText = currentText.replacingCharacters(in: range, with: string)
-
- return newText.count <= LNOrderManager.orderExtraMaxLength
- }
- }
- extension LNCreateOrderViewController {
- private func updateContent(_ detail: LNGameMateSkillDetailVO) {
- avatar.sd_setImage(with: URL(string: detail.avatar))
- nameLabel.text = detail.nickname
- starLabel.text = "\(detail.star)"
-
- let skill = LNGameMateManager.shared.gameCategory(for: detail.categoryCode)
- skillIc.sd_setImage(with: URL(string: skill?.icon ?? ""))
- skillNameLabel.text = detail.categoryName
-
- skillPriceLabel.text = "\(detail.price.toDisplay)"
- curCount = 1
-
- orderButton.isEnabled = true
- }
-
- private func updateContent(_ detail: LNQRCodeDetailResponse) {
- avatar.sd_setImage(with: URL(string: detail.avatar))
- nameLabel.text = detail.nickname
- starLabel.text = "\(detail.star)"
-
- skillIc.sd_setImage(with: URL(string: detail.bizCategoryIcon))
- skillNameLabel.text = detail.bizCategoryName
-
- skillPriceLabel.text = "\(detail.price.toDisplay)"
- curCount = detail.purchaseQty
- if detail.type != .normal {
- minuButton.isEnabled = false
- addButton.isEnabled = false
- }
-
- orderButton.isEnabled = !detail.sellerUserNo.isMyUid
- }
-
- private func updateCost() {
- if let skill {
- let cost = skill.price * Double(curCount)
- let text: String = .init(key: "A00121", cost.toDisplay, curCount, skill.unit)
- let attrStr = NSMutableAttributedString(string: text, attributes: [.font: UIFont.body_s])
- let range = (text as NSString).range(of: "\(cost.toDisplay)")
- attrStr.addAttribute(.font, value: UIFont.heading_h2, range: range)
- costLabel.attributedText = attrStr
- } else if let qrDetail {
- let cost = qrDetail.price * Double(curCount)
- let text: String = .init(key: "A00121", cost.toDisplay, curCount, qrDetail.unit)
- let attrStr = NSMutableAttributedString(string: text, attributes: [.font: UIFont.body_s])
- let range = (text as NSString).range(of: "\(cost.toDisplay)")
- attrStr.addAttribute(.font, value: UIFont.heading_h2, range: range)
- costLabel.attributedText = attrStr
- }
- }
-
- private func setupViews() {
- title = .init(key: "A00127")
- view.backgroundColor = .primary_1
- view.onTap { [weak self] in
- guard let self else { return }
- view.endEditing(true)
- }
-
- let stackView = UIStackView()
- stackView.axis = .vertical
- stackView.spacing = 10
- view.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.directionalHorizontalEdges.equalToSuperview().inset(16)
- make.top.equalToSuperview().offset(16)
- }
-
- let userInfo = buildUserInfo()
- stackView.addArrangedSubview(userInfo)
-
- let skillInfo = buildSkillInfo()
- stackView.addArrangedSubview(skillInfo)
-
- let extra = buildExtra()
- stackView.addArrangedSubview(extra)
-
- let menu = buildMenu()
- view.addSubview(menu)
- menu.snp.makeConstraints { make in
- make.directionalHorizontalEdges.equalToSuperview()
- make.bottom.equalToSuperview()
- }
- }
-
- private func buildUserInfo() -> UIView {
- let container = UIView()
- container.backgroundColor = .fill
- container.layer.cornerRadius = 12
- container.snp.makeConstraints { make in
- make.height.equalTo(60)
- }
-
- let starView = UIView()
- container.addSubview(starView)
- starView.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-12)
- }
-
- let star = UIImageView()
- star.image = .init(named: "ic_star_fill")
- starView.addSubview(star)
- star.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalToSuperview()
- make.width.height.equalTo(16)
- }
-
- starLabel.font = .heading_h3
- starLabel.textColor = .text_5
- starView.addSubview(starLabel)
- starLabel.snp.makeConstraints { make in
- make.verticalEdges.equalToSuperview()
- make.leading.equalTo(star.snp.trailing).offset(4)
- make.trailing.equalToSuperview()
- }
-
- avatar.layer.cornerRadius = 20
- avatar.clipsToBounds = true
- container.addSubview(avatar)
- avatar.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- make.width.height.equalTo(40)
- }
-
- nameLabel.font = .heading_h4
- nameLabel.textColor = .text_5
- container.addSubview(nameLabel)
- nameLabel.snp.makeConstraints { make in
- make.leading.equalTo(avatar.snp.trailing).offset(8)
- make.centerY.equalToSuperview()
- make.trailing.lessThanOrEqualTo(starView.snp.leading).offset(-12)
- }
-
- return container
- }
-
- private func buildSkillInfo() -> UIView {
- let container = UIStackView()
- container.axis = .vertical
- container.spacing = 0
- container.backgroundColor = .fill
- container.layer.cornerRadius = 12
-
- let infoView = UIView()
- infoView.snp.makeConstraints { make in
- make.height.equalTo(40)
- }
- container.addArrangedSubview(infoView)
-
- skillIc.layer.cornerRadius = 13
- skillIc.clipsToBounds = true
- infoView.addSubview(skillIc)
- skillIc.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- make.width.height.equalTo(26)
- }
-
- skillNameLabel.font = .heading_h5
- skillNameLabel.textColor = .text_5
- infoView.addSubview(skillNameLabel)
- skillNameLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalTo(skillIc.snp.trailing).offset(2)
- }
-
- let line = UIView()
- line.backgroundColor = .fill_2
- infoView.addSubview(line)
- line.snp.makeConstraints { make in
- make.directionalHorizontalEdges.equalToSuperview().inset(11)
- make.bottom.equalToSuperview()
- make.height.equalTo(1)
- }
-
- let priceView = UIView()
- priceView.snp.makeConstraints { make in
- make.height.equalTo(38)
- }
- container.addArrangedSubview(priceView)
-
- let priceTitleLabel = UILabel()
- priceTitleLabel.font = .heading_h5
- priceTitleLabel.textColor = .text_5
- priceTitleLabel.text = .init(key: "A00128")
- priceView.addSubview(priceTitleLabel)
- priceTitleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- skillPriceLabel.font = .heading_h3
- skillPriceLabel.textColor = .text_5
- priceView.addSubview(skillPriceLabel)
- skillPriceLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-12)
- }
-
- let coinIc = UIImageView.coinImageView()
- priceView.addSubview(coinIc)
- coinIc.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(skillPriceLabel.snp.leading).offset(-2)
- make.width.height.equalTo(20)
- }
-
- let countView = UIView()
- countView.snp.makeConstraints { make in
- make.height.equalTo(38)
- }
- container.addArrangedSubview(countView)
-
- let countTitleLabel = UILabel()
- countTitleLabel.font = .heading_h5
- countTitleLabel.textColor = .text_5
- countTitleLabel.text = .init(key: "A00122")
- countView.addSubview(countTitleLabel)
- countTitleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- 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 }
- curCount += 1
- }), for: .touchUpInside)
- countView.addSubview(addButton)
- addButton.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-12)
- make.width.height.equalTo(24)
- }
-
- countLabel.font = .body_m
- countLabel.textColor = .text_5
- countView.addSubview(countLabel)
- countLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(addButton.snp.leading).offset(-10)
- }
-
- minuButton.setTitle("-", for: .normal)
- minuButton.setTitleColor(.text_4, for: .normal)
- minuButton.setTitleColor(.text_2, for: .disabled)
- minuButton.backgroundColor = .primary_1
- minuButton.layer.cornerRadius = 12
- minuButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- self.curCount -= 1
- }), for: .touchUpInside)
- countView.addSubview(minuButton)
- minuButton.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(countLabel.snp.leading).offset(-10)
- make.width.height.equalTo(24)
- }
-
- let discountView = UIView()
- discountView.snp.makeConstraints { make in
- make.height.equalTo(38)
- }
- container.addArrangedSubview(discountView)
-
- let discountTitleLabel = UILabel()
- discountTitleLabel.font = .heading_h5
- discountTitleLabel.textColor = .text_5
- discountTitleLabel.text = .init(key: "A00129")
- discountView.addSubview(discountTitleLabel)
- discountTitleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- let discountArrow = UIImageView.arrowImageView(size: 14)
- discountArrow.tintColor = .text_2
- discountView.addSubview(discountArrow)
- discountArrow.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-12)
- }
-
- let discountDescLabel = UILabel()
- discountDescLabel.font = .body_s
- discountDescLabel.text = .init(key: "A00130")
- discountDescLabel.textColor = .text_2
- discountView.addSubview(discountDescLabel)
- discountDescLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(discountArrow.snp.leading).offset(-3)
- }
-
- let emptyView = UIView()
- emptyView.snp.makeConstraints { make in
- make.height.equalTo(10)
- }
- container.addArrangedSubview(emptyView)
-
- return container
- }
-
- private func buildExtra() -> UIView {
- let container = UIView()
- container.backgroundColor = .fill
- container.layer.cornerRadius = 12
- container.snp.makeConstraints { make in
- make.height.equalTo(50)
- }
-
- let titleLabel = UILabel()
- titleLabel.font = .heading_h5
- titleLabel.textColor = .text_5
- titleLabel.text = .init(key: "A00086")
- titleLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
- container.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalToSuperview().offset(12)
- }
-
- extraInput.font = .body_s
- extraInput.textColor = .text_5
- extraInput.placeholder = .init(key: "A00125", LNOrderManager.orderExtraMaxLength)
- extraInput.clearButtonMode = .whileEditing
- extraInput.delegate = self
- container.addSubview(extraInput)
- extraInput.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalTo(titleLabel.snp.trailing).offset(8)
- make.trailing.equalToSuperview().offset(-12)
- }
-
- return container
- }
-
- private func buildMenu() -> UIView {
- let holder = UIView()
-
- let container = UIView()
- container.backgroundColor = .init(hex: "#ECF6FF")
- container.layer.cornerRadius = 23.5
- holder.addSubview(container)
- container.snp.makeConstraints { make in
- make.directionalHorizontalEdges.equalToSuperview().inset(16)
- make.top.equalToSuperview().offset(8)
- make.bottom.equalToSuperview().offset(-view.safeBottomInset - 10)
- }
-
- orderButton.setBackgroundImage(.init(named: "ic_skill_order_long"), 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 }
- let panel = LNCreateOrderPanel()
- if let skill {
- panel.update(skill, count: curCount, extra: extraInput.text ?? "")
- } else if let qrDetail {
- panel.update(qrDetail, count: curCount, extra: extraInput.text ?? "")
- } else { return }
- panel.completionHandler = { [weak self] orderId in
- guard let self else { return }
-
- if let skill {
- view.pushToChat(uid: skill.userNo)
- } else if let qrDetail {
- view.pushToChat(uid: qrDetail.sellerUserNo)
- }
- navigationController?.viewControllers.removeAll { $0 is LNCreateOrderViewController }
- }
- panel.showIn()
- }), for: .touchUpInside)
- container.addSubview(orderButton)
- orderButton.snp.makeConstraints { make in
- make.trailing.equalToSuperview()
- make.verticalEdges.equalToSuperview()
- }
-
- let descView = UIView()
- container.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(16)
- }
-
- costLabel.textColor = .text_4
- priceView.addSubview(costLabel)
- costLabel.snp.makeConstraints { make in
- make.verticalEdges.trailing.equalToSuperview()
- make.leading.equalTo(coin.snp.trailing).offset(4)
- }
-
- return holder
- }
- }
- #if DEBUG
- import SwiftUI
- struct LNCreateOrderViewControllerPreview: UIViewControllerRepresentable {
- func makeUIViewController(context: Context) -> some UIViewController {
- LNCreateOrderViewController()
- }
-
- func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { }
- }
- #Preview(body: {
- LNCreateOrderViewControllerPreview()
- })
- #endif
|