// // LNJoinUsInputPhoneView.swift // Gami // // Created by OneeChan on 2026/1/19. // import Foundation import UIKit import SnapKit protocol LNJoinUsInputPhoneViewDelegate: AnyObject { func joinUsInputPhoneView(view: LNJoinUsInputPhoneView, didFinished code: String, phone: String) } class LNJoinUsInputPhoneView: UIView { private let countryIcon = UIImageView() private let countryCodeLabel = UILabel() private let phoneInputView = LNTextField() private let confirmButton = UIButton() private var sections: [(code: String, list: [LNCountryCodeVO])] = [] weak var delegate: LNJoinUsInputPhoneViewDelegate? override init(frame: CGRect) { super.init(frame: frame) setupViews() LNEventDeliver.addObserver(self) loadCountryCodeList() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNJoinUsInputPhoneView { private func loadCountryCodeList() { LNConfigManager.shared.getCountryCodeList { [weak self] list in guard let self else { return } guard let list else { return } sortList(list) } } private func sortList(_ list: [LNCountryCodeVO]) { sections.removeAll() var map: [String: [LNCountryCodeVO]] = [:] for item in list { let first = item.name.classificationFirstLetter var section = map[first] ?? [] section.append(item) map[first] = section } let keys = map.keys.sorted() for key in keys { sections.append((key, map[key]) as! (code: String, list: [LNCountryCodeVO])) } var item: LNCountryCodeVO? = list.first { $0.code == LNAppConfig.shared.curLang.countryCode } if item == nil { item = list.first { $0.code == LNAppLanguage.indonesian.countryCode } } if let item { countryIcon.sd_setImage(with: URL(string: item.icon)) countryCodeLabel.text = item.num } } } extension LNJoinUsInputPhoneView: LNProfileManagerNotify { func onBindPhoneCaptchaCoolDownChanged(time: Int) { let text: String = if time == 0 { .init(key: "B00023") } else { .init(key: "B00023") + " (\(time)s)" } confirmButton.setTitle(text, for: .normal) checkConfirmButton() } } extension LNJoinUsInputPhoneView { private func checkConfirmButton() { let text = phoneInputView.text ?? "" confirmButton.isEnabled = !text.isEmpty && LNProfileManager.shared.canSendCaptcha } private func setupViews() { onTap { [weak self] in guard let self else { return } endEditing(true) } let titleLabel = UILabel() titleLabel.font = .heading_h2 titleLabel.textColor = .text_5 titleLabel.text = .init(key: "B00035") titleLabel.textAlignment = .center addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.top.equalToSuperview().offset(26) } let phoneInput = buildPhoneInput() addSubview(phoneInput) phoneInput.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(22) make.top.equalTo(titleLabel.snp.bottom).offset(30) } let confirm = buildConfirmButton() addSubview(confirm) confirm.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(22) make.top.equalTo(phoneInput.snp.bottom).offset(24) } } private func buildPhoneInput() -> UIView { let container = UIView() container.backgroundColor = .fill_2 container.layer.cornerRadius = 26 container.snp.makeConstraints { make in make.height.equalTo(52) } let countryView = UIView() countryView.onTap { [weak self] in guard let self else { return } guard !sections.isEmpty else { return } let panel = LNCountrySelectPanel() panel.containerHeight = .height(superview!.bounds.height) panel.update(sections) panel.handler = { [weak self] item in guard let self else { return } countryIcon.sd_setImage(with: URL(string: item.icon)) countryCodeLabel.text = item.num } panel.popup() } container.addSubview(countryView) countryView.snp.makeConstraints { make in make.verticalEdges.equalToSuperview() make.leading.equalToSuperview() } countryIcon.layer.cornerRadius = 12 countryIcon.clipsToBounds = true countryView.addSubview(countryIcon) countryIcon.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalToSuperview().offset(16) make.width.height.equalTo(24) } countryCodeLabel.font = .heading_h2 countryCodeLabel.textColor = .text_5 countryCodeLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) countryCodeLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) countryView.addSubview(countryCodeLabel) countryCodeLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(countryIcon.snp.trailing).offset(4) } let config = UIImage.SymbolConfiguration(pointSize: 10) let arrow = UIImageView() arrow.image = .init(systemName: "chevron.down", withConfiguration: config)?.withRenderingMode(.alwaysTemplate) arrow.tintColor = .text_5 countryView.addSubview(arrow) arrow.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(countryCodeLabel.snp.trailing).offset(4) make.trailing.equalToSuperview() make.width.equalTo(12) } phoneInputView.font = .heading_h2 phoneInputView.textColor = .text_5 phoneInputView.attributedPlaceholder = .init(string: .init(key: "B00021"), attributes: [ .font: UIFont.body_l, .foregroundColor: UIColor.text_2 ]) phoneInputView.clearButtonMode = .always phoneInputView.keyboardType = .numberPad phoneInputView.cursorWidth = 2 phoneInputView.cursorHeight = 18 phoneInputView.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } checkConfirmButton() }), for: .editingChanged) container.addSubview(phoneInputView) phoneInputView.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(countryView.snp.trailing).offset(12) make.trailing.equalToSuperview().offset(-12) } return container } private func buildConfirmButton() -> UIView { confirmButton.setBackgroundImage(.primary_8, for: .normal) confirmButton.layer.cornerRadius = 23.5 confirmButton.clipsToBounds = true confirmButton.setTitle(.init(key: "B00023"), for: .normal) confirmButton.setTitleColor(.text_1, for: .normal) confirmButton.titleLabel?.font = .heading_h3 confirmButton.isEnabled = false confirmButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } let code = countryCodeLabel.text let phone = phoneInputView.text guard let code, let phone else { return } showLoading() LNProfileManager.shared.getBindPhoneCaptcha(code: code, phone: phone) { [weak self] success in dismissLoading() guard let self else { return } guard success else { return } delegate?.joinUsInputPhoneView(view: self, didFinished: code, phone: phone) } }), for: .touchUpInside) confirmButton.snp.makeConstraints { make in make.height.equalTo(47) } return confirmButton } }