|
|
@@ -0,0 +1,487 @@
|
|
|
+//
|
|
|
+// LNGameMateCenterViewController.swift
|
|
|
+// Gami
|
|
|
+//
|
|
|
+// Created by OneeChan on 2026/1/22.
|
|
|
+//
|
|
|
+
|
|
|
+import Foundation
|
|
|
+import UIKit
|
|
|
+import SnapKit
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+extension UIView {
|
|
|
+ func pushToMateCenter() {
|
|
|
+ let vc = LNGameMateCenterViewController()
|
|
|
+ navigationController?.pushViewController(vc, animated: true)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+class LNGameMateCenterViewController: LNViewController {
|
|
|
+ private let incomeLabel = UILabel()
|
|
|
+ private let exposureLabel = UILabel()
|
|
|
+ private let visitorLabel = UILabel()
|
|
|
+
|
|
|
+ private let statusButton = UIButton()
|
|
|
+ private let statusLabel = UILabel()
|
|
|
+ private let statusDescLabel = UILabel()
|
|
|
+
|
|
|
+ private let skillStackView = UIStackView()
|
|
|
+
|
|
|
+ override func viewDidLoad() {
|
|
|
+ super.viewDidLoad()
|
|
|
+
|
|
|
+ setupViews()
|
|
|
+
|
|
|
+ LNEventDeliver.addObserver(self)
|
|
|
+
|
|
|
+ onUserInfoChanged(userInfo: myUserInfo)
|
|
|
+ onGameMateManagerInfoChanged()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension LNGameMateCenterViewController: LNProfileManagerNotify, LNGameMateManagerNotify {
|
|
|
+ func onUserInfoChanged(userInfo: LNUserProfileVO) {
|
|
|
+ guard userInfo.userNo.isMyUid else { return }
|
|
|
+
|
|
|
+ skillStackView.arrangedSubviews.forEach {
|
|
|
+ skillStackView.removeArrangedSubview($0)
|
|
|
+ $0.removeFromSuperview()
|
|
|
+ }
|
|
|
+ skillStackView.superview?.isHidden = userInfo.skills.isEmpty
|
|
|
+
|
|
|
+ for skill in userInfo.skills {
|
|
|
+ let view = buildSkillItem(skill)
|
|
|
+ skillStackView.addArrangedSubview(view)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func onGameMateManagerInfoChanged() {
|
|
|
+ guard let myGameMateInfo else { return }
|
|
|
+
|
|
|
+ incomeLabel.text = myGameMateInfo.weekBeanIncome.toDisplay
|
|
|
+ exposureLabel.text = "\(myGameMateInfo.exposureCountDay)"
|
|
|
+ visitorLabel.text = "\(myGameMateInfo.visitorCount)"
|
|
|
+
|
|
|
+ if myGameMateInfo.playmateOpen {
|
|
|
+ statusButton.setBackgroundImage(.primary_7, for: .normal)
|
|
|
+ statusLabel.text = .init(key: "B00087")
|
|
|
+ statusDescLabel.text = .init(key: "B00088")
|
|
|
+ } else {
|
|
|
+ statusButton.setBackgroundImage(.primary_8, for: .normal)
|
|
|
+ statusLabel.text = .init(key: "B00089")
|
|
|
+ statusDescLabel.text = .init(key: "B00090")
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension LNGameMateCenterViewController {
|
|
|
+ private func setupViews() {
|
|
|
+ navigationBarColor = .clear
|
|
|
+ view.backgroundColor = .primary_1
|
|
|
+ title = .init(key: "B00071")
|
|
|
+
|
|
|
+ let topCover = UIImageView()
|
|
|
+ topCover.image = .icHomeTopBg
|
|
|
+ topCover.backgroundColor = .primary_1
|
|
|
+ view.addSubview(topCover)
|
|
|
+ topCover.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview()
|
|
|
+ make.top.equalToSuperview().offset(-(UIView.navigationBarHeight + UIView.statusBarHeight))
|
|
|
+ }
|
|
|
+
|
|
|
+ let scrollView = UIScrollView()
|
|
|
+ scrollView.showsVerticalScrollIndicator = false
|
|
|
+ scrollView.showsHorizontalScrollIndicator = false
|
|
|
+ scrollView.contentInset = .init(top: 0, left: 0, bottom: -view.commonBottomInset, right: 0)
|
|
|
+ view.addSubview(scrollView)
|
|
|
+ scrollView.snp.makeConstraints { make in
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(16)
|
|
|
+ }
|
|
|
+
|
|
|
+ let stackView = UIStackView()
|
|
|
+ stackView.axis = .vertical
|
|
|
+ stackView.spacing = 12
|
|
|
+ scrollView.addSubview(stackView)
|
|
|
+ stackView.snp.makeConstraints { make in
|
|
|
+ make.edges.equalToSuperview()
|
|
|
+ make.width.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ stackView.addArrangedSubview(buildRecordInfos())
|
|
|
+ stackView.addArrangedSubview(buildSkillList())
|
|
|
+ stackView.addArrangedSubview(buildOrderRecord())
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildRecordInfos() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+ container.layer.cornerRadius = 12
|
|
|
+ container.backgroundColor = .fill
|
|
|
+
|
|
|
+ let stackView = UIStackView()
|
|
|
+ stackView.axis = .vertical
|
|
|
+ stackView.spacing = 20
|
|
|
+ container.addSubview(stackView)
|
|
|
+ stackView.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(10)
|
|
|
+ make.verticalEdges.equalToSuperview().inset(20)
|
|
|
+ }
|
|
|
+
|
|
|
+ stackView.addArrangedSubview(buildIncome())
|
|
|
+ stackView.addArrangedSubview(buildDataView())
|
|
|
+ stackView.addArrangedSubview(buildOpenButton())
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildIncome() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+
|
|
|
+ let incomeView = UIView()
|
|
|
+ container.addSubview(incomeView)
|
|
|
+ incomeView.snp.makeConstraints { make in
|
|
|
+ make.top.equalToSuperview()
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let beanIc = UIImageView.beanImageView()
|
|
|
+ incomeView.addSubview(beanIc)
|
|
|
+ beanIc.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalToSuperview()
|
|
|
+ make.width.height.equalTo(28)
|
|
|
+ }
|
|
|
+
|
|
|
+ incomeLabel.text = "0"
|
|
|
+ incomeLabel.font = .systemFont(ofSize: 30, weight: .semibold)
|
|
|
+ incomeLabel.textColor = .text_5
|
|
|
+ incomeView.addSubview(incomeLabel)
|
|
|
+ incomeLabel.snp.makeConstraints { make in
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ make.leading.equalTo(beanIc.snp.trailing)
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let descView = UIView()
|
|
|
+ container.addSubview(descView)
|
|
|
+ descView.snp.makeConstraints { make in
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
+ make.top.equalTo(incomeView.snp.bottom).offset(4)
|
|
|
+ make.bottom.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let descLabel = UILabel()
|
|
|
+ descLabel.text = .init(key: "B00073")
|
|
|
+ descLabel.font = .body_m
|
|
|
+ descLabel.textColor = .text_5
|
|
|
+ descView.addSubview(descLabel)
|
|
|
+ descLabel.snp.makeConstraints { make in
|
|
|
+ make.leading.equalToSuperview()
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let arrow = UIImageView.arrowImageView(size: 12)
|
|
|
+ arrow.tintColor = .text_4
|
|
|
+ descView.addSubview(arrow)
|
|
|
+ arrow.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
+ make.leading.equalTo(descLabel.snp.trailing).offset(4)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildDataView() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+ container.onTap { [weak self] in
|
|
|
+ guard let self else { return }
|
|
|
+ view.pushToVisitors()
|
|
|
+ }
|
|
|
+
|
|
|
+ let stackView = UIStackView()
|
|
|
+ stackView.axis = .horizontal
|
|
|
+ stackView.distribution = .fillEqually
|
|
|
+ container.addSubview(stackView)
|
|
|
+ stackView.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(5)
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ stackView.addArrangedSubview(buildDataItemView(.init(key: "B00073"), contentLabel: exposureLabel))
|
|
|
+ stackView.addArrangedSubview(buildDataItemView(.init(key: "B00074"), contentLabel: visitorLabel))
|
|
|
+
|
|
|
+ let line = buildLine()
|
|
|
+ stackView.addSubview(line)
|
|
|
+ line.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildDataItemView(_ title: String, contentLabel: UILabel) -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+
|
|
|
+ contentLabel.text = "0"
|
|
|
+ contentLabel.font = .heading_h2
|
|
|
+ contentLabel.textColor = .text_5
|
|
|
+ container.addSubview(contentLabel)
|
|
|
+ contentLabel.snp.makeConstraints { make in
|
|
|
+ make.top.equalToSuperview()
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let descView = UIView()
|
|
|
+ container.addSubview(descView)
|
|
|
+ descView.snp.makeConstraints { make in
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
+ make.top.equalTo(contentLabel.snp.bottom).offset(6)
|
|
|
+ make.bottom.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ let descLabel = UILabel()
|
|
|
+ descLabel.text = title
|
|
|
+ descLabel.font = .body_s
|
|
|
+ descLabel.textColor = .text_4
|
|
|
+ descView.addSubview(descLabel)
|
|
|
+ descLabel.snp.makeConstraints { make in
|
|
|
+ make.leading.equalToSuperview()
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let arrow = UIImageView.arrowImageView(size: 10)
|
|
|
+ arrow.tintColor = .text_4
|
|
|
+ descView.addSubview(arrow)
|
|
|
+ arrow.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
+ make.leading.equalTo(descLabel.snp.trailing).offset(4)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildOpenButton() -> UIView {
|
|
|
+ statusButton.layer.cornerRadius = 26
|
|
|
+ statusButton.clipsToBounds = true
|
|
|
+ statusButton.setBackgroundImage(.primary_8, for: .normal)
|
|
|
+ statusButton.addAction(UIAction(handler: { _ in
|
|
|
+ showLoading()
|
|
|
+ let isOpen = myGameMateInfo?.playmateOpen == true
|
|
|
+ LNGameMateManager.shared.enableGameMate(open: !isOpen) { success in
|
|
|
+ dismissLoading()
|
|
|
+ }
|
|
|
+ }), for: .touchUpInside)
|
|
|
+ statusButton.snp.makeConstraints { make in
|
|
|
+ make.height.equalTo(56)
|
|
|
+ }
|
|
|
+
|
|
|
+ let container = UIView()
|
|
|
+ container.isUserInteractionEnabled = false
|
|
|
+ statusButton.addSubview(container)
|
|
|
+ container.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ make.leading.greaterThanOrEqualToSuperview().offset(16)
|
|
|
+ }
|
|
|
+
|
|
|
+ statusLabel.font = .heading_h3
|
|
|
+ statusLabel.textColor = .text_1
|
|
|
+ statusLabel.textAlignment = .center
|
|
|
+ container.addSubview(statusLabel)
|
|
|
+ statusLabel.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview()
|
|
|
+ make.top.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ statusDescLabel.font = .body_xs
|
|
|
+ statusDescLabel.textColor = .primary_1
|
|
|
+ container.addSubview(statusDescLabel)
|
|
|
+ statusDescLabel.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview()
|
|
|
+ make.bottom.equalToSuperview()
|
|
|
+ make.top.equalTo(statusLabel.snp.bottom)
|
|
|
+ }
|
|
|
+
|
|
|
+ return statusButton
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildLine() -> UIView {
|
|
|
+ let line = UIView()
|
|
|
+ line.backgroundColor = .init(hex: "#D9D9D9")
|
|
|
+ line.snp.makeConstraints { make in
|
|
|
+ make.width.equalTo(1)
|
|
|
+ make.height.equalTo(37)
|
|
|
+ }
|
|
|
+
|
|
|
+ return line
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildSkillList() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+ container.layer.cornerRadius = 12
|
|
|
+ container.backgroundColor = .fill
|
|
|
+
|
|
|
+ let stackView = UIStackView()
|
|
|
+ stackView.axis = .vertical
|
|
|
+ container.addSubview(stackView)
|
|
|
+ stackView.snp.makeConstraints { make in
|
|
|
+ make.edges.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let header = buildSkillHeader()
|
|
|
+ stackView.addArrangedSubview(header)
|
|
|
+
|
|
|
+ let skillContainer = UIView()
|
|
|
+ stackView.addArrangedSubview(skillContainer)
|
|
|
+
|
|
|
+ skillStackView.axis = .vertical
|
|
|
+ skillStackView.spacing = 8
|
|
|
+ skillContainer.addSubview(skillStackView)
|
|
|
+ skillStackView.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(12)
|
|
|
+ make.top.equalToSuperview().offset(4)
|
|
|
+ make.bottom.equalToSuperview().offset(-12)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildSkillHeader() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+ container.onTap { [weak self] in
|
|
|
+ guard let self else { return }
|
|
|
+ view.pushToSkillManager()
|
|
|
+ }
|
|
|
+ container.snp.makeConstraints { make in
|
|
|
+ make.height.equalTo(40)
|
|
|
+ }
|
|
|
+
|
|
|
+ let titleLabel = UILabel()
|
|
|
+ titleLabel.text = .init(key: "B00075")
|
|
|
+ titleLabel.font = .heading_h3
|
|
|
+ titleLabel.textColor = .text_5
|
|
|
+ container.addSubview(titleLabel)
|
|
|
+ titleLabel.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalToSuperview().offset(16)
|
|
|
+ }
|
|
|
+
|
|
|
+ let arrow = UIImageView.arrowImageView(size: 12)
|
|
|
+ arrow.tintColor = .text_4
|
|
|
+ container.addSubview(arrow)
|
|
|
+ arrow.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.trailing.equalToSuperview().offset(-16)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildSkillItem(_ skill: LNGameMateSkillVO) -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+ container.layer.cornerRadius = 12
|
|
|
+ container.layer.borderColor = .fill_4
|
|
|
+ container.layer.borderWidth = 1
|
|
|
+ container.snp.makeConstraints { make in
|
|
|
+ make.height.equalTo(54)
|
|
|
+ }
|
|
|
+
|
|
|
+ let ic = UIImageView()
|
|
|
+ ic.sd_setImage(with: URL(string: skill.icon))
|
|
|
+ container.addSubview(ic)
|
|
|
+ ic.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalToSuperview().offset(10)
|
|
|
+ make.width.height.equalTo(50)
|
|
|
+ }
|
|
|
+
|
|
|
+ let priceView = UIView()
|
|
|
+ container.addSubview(priceView)
|
|
|
+ priceView.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.trailing.equalToSuperview().offset(-10)
|
|
|
+ }
|
|
|
+
|
|
|
+ let coin = UIImageView.coinImageView()
|
|
|
+ priceView.addSubview(coin)
|
|
|
+ coin.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalToSuperview()
|
|
|
+ make.width.height.equalTo(16)
|
|
|
+ }
|
|
|
+
|
|
|
+ let priceLabel = UILabel()
|
|
|
+ priceLabel.text = skill.price.toDisplay
|
|
|
+ priceLabel.font = .heading_h4
|
|
|
+ priceLabel.textColor = .text_5
|
|
|
+ priceView.addSubview(priceLabel)
|
|
|
+ priceLabel.snp.makeConstraints { make in
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ make.leading.equalTo(coin.snp.trailing).offset(1)
|
|
|
+ }
|
|
|
+
|
|
|
+ let unitLabel = UILabel()
|
|
|
+ unitLabel.text = "/1 \(skill.unit)"
|
|
|
+ unitLabel.font = .body_s
|
|
|
+ unitLabel.textColor = .text_4
|
|
|
+ priceView.addSubview(unitLabel)
|
|
|
+ unitLabel.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalTo(priceLabel.snp.trailing).offset(1)
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let nameLabel = UILabel()
|
|
|
+ nameLabel.text = skill.name
|
|
|
+ nameLabel.font = .heading_h4
|
|
|
+ nameLabel.textColor = .text_5
|
|
|
+ nameLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
+ nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
+ container.addSubview(nameLabel)
|
|
|
+ nameLabel.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalTo(ic.snp.trailing).offset(4)
|
|
|
+ make.trailing.lessThanOrEqualTo(priceView.snp.leading).offset(-20)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildOrderRecord() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+ container.layer.cornerRadius = 12
|
|
|
+ container.backgroundColor = .fill
|
|
|
+ container.onTap { [weak self] in
|
|
|
+ guard let self else { return }
|
|
|
+ view.pushToOrderRecord()
|
|
|
+ }
|
|
|
+ container.snp.makeConstraints { make in
|
|
|
+ make.height.equalTo(40)
|
|
|
+ }
|
|
|
+
|
|
|
+ let titleLabel = UILabel()
|
|
|
+ titleLabel.text = .init(key: "A00209")
|
|
|
+ titleLabel.font = .heading_h3
|
|
|
+ titleLabel.textColor = .text_5
|
|
|
+ container.addSubview(titleLabel)
|
|
|
+ titleLabel.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalToSuperview().offset(16)
|
|
|
+ }
|
|
|
+
|
|
|
+ let arrow = UIImageView.arrowImageView(size: 12)
|
|
|
+ arrow.tintColor = .text_4
|
|
|
+ container.addSubview(arrow)
|
|
|
+ arrow.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.trailing.equalToSuperview().offset(-16)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+}
|