| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- //
- // LNCreateOrderPanel.swift
- // Lanu
- //
- // Created by OneeChan on 2025/12/22.
- //
- import Foundation
- import UIKit
- import SnapKit
- class LNCreateOrderPanel: LNPopupView {
- private let gameNameLabel = UILabel()
-
- private let avatar = UIImageView()
- private let nameLabel = UILabel()
- private let priceLabel = UILabel()
-
- private let countEditView = UIView()
- private let minuButton = UIButton()
- private let customCountLabel = UILabel()
- private let addButton = UIButton()
-
- private let countLabel = UILabel()
- private let costLabel = UILabel()
-
- private let discountView = UIView()
- private let newbieView = LNNewbieDiscountView()
- private let discountCoinLabel = UILabel()
-
- private let extraView = UIView()
- private let extraInput = UITextField()
-
- private let curCoinLabel = UILabel()
-
- private var skillId: String?
- private var qrCode: String?
- private var price: Double = 0.0
- private var curCount = 1 {
- didSet {
- countLabel.text = "x\(curCount)"
- customCountLabel.text = "\(curCount)"
-
- if let discount = LNOrderManager.shared.discountFor(price) {
- costLabel.text = (price * Double(curCount) - (1 - discount) * price).toDisplay
- } else {
- costLabel.text = (price * Double(curCount)).toDisplay
- }
- if curCount <= 1 {
- minuButton.isEnabled = false
- } else {
- minuButton.isEnabled = true
- }
- }
- }
-
- var editable: Bool = false {
- didSet {
- extraView.isHidden = !editable
- countEditView.isHidden = !editable
- countLabel.isHidden = editable
- }
- }
- var scene: LNOrderScene?
-
- private var targetUid: String?
- var completionHandler: ((String) -> Void)?
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- setupViews()
-
- LNEventDeliver.addObserver(self)
- LNPurchaseManager.shared.reloadWalletInfo()
- }
-
- func update(_ detail: LNGameMateSkillDetailVO, count: Int, extra: String) {
- gameNameLabel.text = detail.categoryName
- avatar.showAvatar(detail.avatar)
- nameLabel.text = detail.nickname
- priceLabel.text = "\(detail.price.toDisplay)/\(detail.unit)"
-
- if let discount = LNOrderManager.shared.discountFor(detail.price) {
- discountView.isHidden = false
- newbieView.update(1 - discount)
- discountCoinLabel.text = "-\(detail.price * (1 - discount))"
- } else {
- discountView.isHidden = true
- }
-
- skillId = detail.id
- price = detail.price
- targetUid = detail.userNo
- curCount = count
-
- extraInput.text = extra
- }
-
- func update(_ skill: LNGameMateSkillVO, user: LNUserProfileVO) {
- gameNameLabel.text = skill.name
- avatar.showAvatar(user.avatar)
- nameLabel.text = user.nickname
- priceLabel.text = "\(skill.price.toDisplay)/\(skill.unit)"
-
- if let discount = LNOrderManager.shared.discountFor(skill.price) {
- discountView.isHidden = false
- newbieView.update(1 - discount)
- discountCoinLabel.text = "-\(skill.price * (1 - discount))"
- } else {
- discountView.isHidden = true
- }
-
- skillId = skill.id
- price = skill.price
- targetUid = user.userNo
- curCount = 1
- }
-
- func update(_ detail: LNQRCodeDetailResponse, count: Int, extra: String) {
- gameNameLabel.text = detail.bizCategoryName
- avatar.showAvatar(detail.avatar)
- nameLabel.text = detail.nickname
- priceLabel.text = "\(detail.price.toDisplay)/\(detail.unit)"
-
- if let discount = LNOrderManager.shared.discountFor(detail.price) {
- discountView.isHidden = false
- newbieView.update(1 - discount)
- discountCoinLabel.text = "-\(detail.price * (1 - discount))"
- } else {
- discountView.isHidden = true
- }
-
- qrCode = detail.qrCode
- price = detail.price
- targetUid = detail.sellerUserNo
- curCount = count
-
- extraInput.text = extra
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- extension LNCreateOrderPanel: LNPurchaseManagerNotify {
- func onUserWalletInfoChanged(info: LNUserWalletInfo) {
- curCoinLabel.text = info.coin.toDisplay
- }
- }
- extension LNCreateOrderPanel: 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)
- if newText.count < currentText.count {
- return true
- }
-
- return newText.count <= LNOrderManager.orderExtraMaxLength
- }
-
- func textFieldShouldReturn(_ textField: UITextField) -> Bool {
- textField.resignFirstResponder()
- return true
- }
- }
- extension LNCreateOrderPanel {
- private func setupViews() {
- container.backgroundColor = .primary_1
-
- let stackView = UIStackView()
- stackView.axis = .vertical
- stackView.spacing = 10
- container.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(12)
- make.top.equalToSuperview().offset(20)
- }
-
- let billInfo = buildBillInfo()
- stackView.addArrangedSubview(billInfo)
-
- let extra = buildExtra()
- stackView.addArrangedSubview(extra)
-
- let wallet = buildWalletInfo()
- stackView.addArrangedSubview(wallet)
-
- let confirmButton = UIButton()
- confirmButton.setTitle(.init(key: "A00123"), for: .normal)
- confirmButton.setTitleColor(.text_1, for: .normal)
- confirmButton.titleLabel?.font = .heading_h3
- confirmButton.setBackgroundImage(.primary_8, for: .normal)
- confirmButton.layer.cornerRadius = 23.5
- confirmButton.clipsToBounds = true
- confirmButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- let extra = extraInput.text ?? ""
-
- let handler = { [weak self] (orderNo: String?) in
- guard let self else { return }
- guard let orderNo else { return }
- dismiss()
- if let targetUid,
- !(navigationController?.topViewController is LNIMChatViewController) {
- pushToChat(uid: targetUid)
- }
- completionHandler?(orderNo)
- }
- if let skillId {
- LNOrderManager.shared.createOrder(
- skillId: skillId, count: curCount, remark: extra, scene: scene, handler: handler)
- } else if let qrCode {
- LNOrderManager.shared.createQRCodeOrder(
- data: qrCode, count: curCount, extra: extra, scene: scene, handler: handler)
- }
- }), for: .touchUpInside)
- container.addSubview(confirmButton)
- confirmButton.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(12)
- make.top.equalTo(stackView.snp.bottom).offset(15)
- make.bottom.equalToSuperview().offset(commonBottomInset)
- make.height.equalTo(47)
- }
- }
-
- private func buildBillInfo() -> UIView {
- let container = UIView()
- container.backgroundColor = .fill
- container.layer.cornerRadius = 12
- container.onTap { [weak self] in
- guard let self else { return }
- endEditing(true)
- }
-
- let stackView = UIStackView()
- stackView.axis = .vertical
- container.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- stackView.addArrangedSubview(buildUserInfo())
- stackView.addArrangedSubview(buildPrice())
- stackView.addArrangedSubview(buildDiscount())
- stackView.addArrangedSubview(buildCost())
-
- return container
- }
-
- private func buildUserInfo() -> UIView {
- let container = UIView()
- container.isUserInteractionEnabled = false
- container.snp.makeConstraints { make in
- make.height.equalTo(83)
- }
-
- let skillTag = UIImageView()
- skillTag.image = .icOrderSkillBg
- container.addSubview(skillTag)
- skillTag.snp.makeConstraints { make in
- make.leading.top.equalToSuperview()
- }
-
- avatar.layer.cornerRadius = 30
- avatar.layer.borderWidth = 2
- avatar.layer.borderColor = UIColor.fill.cgColor
- avatar.backgroundColor = .fill
- avatar.clipsToBounds = true
- container.addSubview(avatar)
- avatar.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.top.equalToSuperview().offset(-8)
- make.width.height.equalTo(60)
- }
-
- gameNameLabel.font = .heading_h5
- gameNameLabel.textColor = .text_1
- gameNameLabel.textAlignment = .center
- skillTag.addSubview(gameNameLabel)
- gameNameLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.horizontalEdges.equalToSuperview().inset(14)
- make.width.lessThanOrEqualTo(82)
- }
-
- nameLabel.font = .heading_h4
- nameLabel.textColor = .text_5
- container.addSubview(nameLabel)
- nameLabel.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.top.equalTo(avatar.snp.bottom).offset(3)
- }
-
- let line = UIView()
- line.backgroundColor = .fill_2
- container.addSubview(line)
- line.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(12)
- make.height.equalTo(1)
- make.bottom.equalToSuperview()
- }
-
- return container
- }
-
- private func buildPrice() -> UIView {
- let container = UIView()
- container.snp.makeConstraints { make in
- make.height.equalTo(49)
- }
-
- let titleLabel = UILabel()
- titleLabel.font = .heading_h5
- titleLabel.textColor = .text_5
- titleLabel.text = .init(key: "A00128")
- container.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- let coin = UIImageView.coinImageView()
- container.addSubview(coin)
- coin.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalTo(titleLabel.snp.trailing).offset(6)
- make.width.height.equalTo(18)
- }
-
- priceLabel.font = .body_m
- priceLabel.textColor = .text_5
- container.addSubview(priceLabel)
- priceLabel.snp.makeConstraints { make in
- make.leading.equalTo(coin.snp.trailing).offset(2)
- make.centerY.equalToSuperview()
- }
-
- countLabel.font = .body_m
- countLabel.textColor = .text_5
- container.addSubview(countLabel)
- countLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-16)
- }
-
- countEditView.isHidden = true
- container.addSubview(countEditView)
- countEditView.snp.makeConstraints { make in
- make.verticalEdges.equalToSuperview()
- make.trailing.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)
- countEditView.addSubview(addButton)
- addButton.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-12)
- make.width.height.equalTo(24)
- }
-
- customCountLabel.font = .body_m
- customCountLabel.textColor = .text_5
- countEditView.addSubview(customCountLabel)
- customCountLabel.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)
- countEditView.addSubview(minuButton)
- minuButton.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(customCountLabel.snp.leading).offset(-10)
- make.width.height.equalTo(24)
- make.leading.equalToSuperview()
- }
-
- return container
- }
-
- private func buildDiscount() -> UIView {
- discountView.snp.makeConstraints { make in
- make.height.equalTo(49)
- }
-
- let titleLabel = UILabel()
- titleLabel.font = .heading_h5
- titleLabel.textColor = .text_5
- titleLabel.text = .init(key: "A00129")
- discountView.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- discountView.addSubview(newbieView)
- newbieView.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalTo(titleLabel.snp.trailing).offset(8)
- }
-
- discountCoinLabel.font = .heading_h3
- discountCoinLabel.textColor = .init(hex: "#FF6F32")
- discountView.addSubview(discountCoinLabel)
- discountCoinLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-12)
- }
-
- let coin = UIImageView.coinImageView()
- discountView.addSubview(coin)
- coin.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(discountCoinLabel.snp.leading).offset(-2)
- make.width.height.equalTo(20)
- }
-
- return discountView
- }
-
- private func buildCost() -> UIView {
- let container = UIView()
- container.isUserInteractionEnabled = false
- container.snp.makeConstraints { make in
- make.height.equalTo(49)
- }
-
- costLabel.font = .heading_h1
- costLabel.textColor = .text_5
- container.addSubview(costLabel)
- costLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-12)
- }
-
- let coin = UIImageView.coinImageView()
- container.addSubview(coin)
- coin.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(costLabel.snp.leading).offset(-2)
- make.width.height.equalTo(24)
- }
-
- let totalLabel = UILabel()
- totalLabel.text = .init(key: "A00124")
- totalLabel.font = .body_m
- totalLabel.textColor = .text_5
- container.addSubview(totalLabel)
- totalLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(coin.snp.leading).offset(-2)
- }
-
- return container
- }
-
- private func buildExtra() -> UIView {
- extraView.isHidden = true
- extraView.backgroundColor = .fill
- extraView.layer.cornerRadius = 12
- extraView.snp.makeConstraints { make in
- make.height.equalTo(50)
- }
-
- let titleLabel = UILabel()
- titleLabel.font = .heading_h5
- titleLabel.text = .init(key: "A00086")
- titleLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
- titleLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
- titleLabel.textColor = .text_5
- extraView.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- extraInput.placeholder = .init(key: "A00125", LNOrderManager.orderExtraMaxLength)
- extraInput.font = .body_s
- extraInput.textColor = .text_5
- extraInput.clearButtonMode = .whileEditing
- extraInput.delegate = self
- extraInput.visibleView = extraView
- extraView.addSubview(extraInput)
- extraInput.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalTo(titleLabel.snp.trailing).offset(8)
- make.trailing.equalToSuperview().offset(-12)
- }
-
- return extraView
- }
-
- private func buildWalletInfo() -> UIView {
- let container = UIView()
- container.isUserInteractionEnabled = false
- container.backgroundColor = .fill
- container.layer.cornerRadius = 12
- container.snp.makeConstraints { make in
- make.height.equalTo(66)
- }
-
- let icon = UIImageView()
- icon.image = .icWalletWithBg
- container.addSubview(icon)
- icon.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalToSuperview().offset(12)
- }
-
- let infoView = UIView()
- container.addSubview(infoView)
- infoView.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalTo(icon.snp.trailing).offset(6)
- }
-
- let titleLabel = UILabel()
- titleLabel.text = .init(key: "A00126")
- titleLabel.font = .heading_h5
- titleLabel.textColor = .text_5
- infoView.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.leading.top.equalToSuperview()
- make.trailing.equalToSuperview()
- }
-
- curCoinLabel.text = myWalletInfo.coin.toDisplay
- curCoinLabel.font = .body_m
- curCoinLabel.textColor = .text_3
- infoView.addSubview(curCoinLabel)
- curCoinLabel.snp.makeConstraints { make in
- make.trailing.bottom.equalToSuperview()
- make.top.equalTo(titleLabel.snp.bottom).offset(2)
- }
-
- let coin = UIImageView.coinImageView()
- infoView.addSubview(coin)
- coin.snp.makeConstraints { make in
- make.centerY.equalTo(curCoinLabel)
- make.leading.equalToSuperview()
- make.trailing.equalTo(curCoinLabel.snp.leading).offset(-2)
- make.width.height.equalTo(16)
- }
-
- let check = UIImageView()
- check.image = .icCheck
- container.addSubview(check)
- check.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-12)
- make.width.height.equalTo(22)
- }
-
- return container
- }
- }
- #if DEBUG
- import SwiftUI
- struct LNCreateOrderPanelPreview: UIViewRepresentable {
- func makeUIView(context: Context) -> some UIView {
- let container = UIView()
- container.backgroundColor = .lightGray
-
- let view = LNCreateOrderPanel()
- view.popup(container)
-
- return container
- }
-
- func updateUIView(_ uiView: UIViewType, context: Context) { }
- }
- #Preview(body: {
- LNCreateOrderPanelPreview()
- })
- #endif // DEBUG
|