|
|
@@ -0,0 +1,296 @@
|
|
|
+//
|
|
|
+// LNRoomGiftHeaderView.swift
|
|
|
+// Gami
|
|
|
+//
|
|
|
+// Created by OneeChan on 2026/3/23.
|
|
|
+//
|
|
|
+
|
|
|
+import Foundation
|
|
|
+import UIKit
|
|
|
+import SnapKit
|
|
|
+
|
|
|
+
|
|
|
+class LNRoomGiftHeaderView: UIView {
|
|
|
+ private let roomSeatsView = UIView()
|
|
|
+ private let stackView = UIStackView()
|
|
|
+
|
|
|
+ private let specifiedUserView = LNRoomGiftSpecifiedUserView()
|
|
|
+
|
|
|
+ private weak var roomSession: LNRoomViewModel?
|
|
|
+ private var headers: [LNRoomSeatNum: LNRoomGiftAvatarView] = [:]
|
|
|
+ var selection: [String] {
|
|
|
+ if !roomSeatsView.isHidden {
|
|
|
+ headers.map { $1 }.filter { !$0.isHidden && $0.isSelected }.compactMap { $0.curSeatItem?.uid }
|
|
|
+ } else if let uid = specifiedUserView.curUid {
|
|
|
+ [uid]
|
|
|
+ } else {
|
|
|
+ []
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override init(frame: CGRect) {
|
|
|
+ super.init(frame: frame)
|
|
|
+
|
|
|
+ setupViews()
|
|
|
+ LNEventDeliver.addObserver(self)
|
|
|
+ }
|
|
|
+
|
|
|
+ func update(_ room: LNRoomViewModel?, selectedUid: String?) {
|
|
|
+ roomSession = room
|
|
|
+ if selectedUid == nil
|
|
|
+ || room?.seatsInfo.contains(where: { $0.uid == selectedUid }) == true {
|
|
|
+ onRoomSeatsChanged()
|
|
|
+
|
|
|
+ roomSeatsView.isHidden = false
|
|
|
+ specifiedUserView.isHidden = true
|
|
|
+ } else {
|
|
|
+ roomSeatsView.isHidden = true
|
|
|
+ specifiedUserView.isHidden = false
|
|
|
+ }
|
|
|
+ if let selectedUid {
|
|
|
+ headers.first { $1.curSeatItem?.uid == selectedUid }?.value.isSelected = true
|
|
|
+ specifiedUserView.update(selectedUid)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ required init?(coder: NSCoder) {
|
|
|
+ fatalError("init(coder:) has not been implemented")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension LNRoomGiftHeaderView: LNRoomViewModelNotify {
|
|
|
+ func onRoomSeatsChanged() {
|
|
|
+ guard let seats = roomSession?.seatsInfo.sorted(by: { $0.index.rawValue < $1.index.rawValue }) else { return }
|
|
|
+ if headers.count != seats.count {
|
|
|
+ stackView.arrangedSubviews.forEach {
|
|
|
+ stackView.removeArrangedSubview($0)
|
|
|
+ $0.removeFromSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ for seat in seats {
|
|
|
+ let avatar = LNRoomGiftAvatarView()
|
|
|
+ avatar.isSelected = false
|
|
|
+ avatar.onTap { [weak avatar] in
|
|
|
+ guard let avatar else { return }
|
|
|
+ avatar.isSelected.toggle()
|
|
|
+ }
|
|
|
+ avatar.update(seat)
|
|
|
+ stackView.addArrangedSubview(avatar)
|
|
|
+ headers[seat.index] = avatar
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for seat in seats {
|
|
|
+ headers[seat.index]?.update(seat)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private extension LNRoomGiftHeaderView {
|
|
|
+ func setupViews() {
|
|
|
+ let titleLabel = UILabel()
|
|
|
+ titleLabel.font = .body_l
|
|
|
+ titleLabel.textColor = .text_2
|
|
|
+ titleLabel.text = .init(key: "B00129")
|
|
|
+ addSubview(titleLabel)
|
|
|
+ titleLabel.snp.makeConstraints { make in
|
|
|
+ make.leading.equalToSuperview().offset(10)
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let roomSeatsView = buildRoomSeatsView()
|
|
|
+ addSubview(roomSeatsView)
|
|
|
+ roomSeatsView.snp.makeConstraints { make in
|
|
|
+ make.leading.equalTo(titleLabel.snp.trailing).offset(6)
|
|
|
+ make.trailing.equalToSuperview().offset(-10)
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ make.height.equalTo(40)
|
|
|
+ }
|
|
|
+
|
|
|
+ let userView = buildSpecifiedUserView()
|
|
|
+ addSubview(userView)
|
|
|
+ userView.snp.makeConstraints { make in
|
|
|
+ make.leading.equalTo(titleLabel.snp.trailing).offset(6)
|
|
|
+ make.trailing.equalToSuperview().offset(-10)
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildRoomSeatsView() -> UIView {
|
|
|
+ let scrollView = UIScrollView()
|
|
|
+ scrollView.showsHorizontalScrollIndicator = false
|
|
|
+ scrollView.contentInset = .init(top: 0, left: 0, bottom: 0, right: 32)
|
|
|
+ roomSeatsView.addSubview(scrollView)
|
|
|
+ scrollView.snp.makeConstraints { make in
|
|
|
+ make.edges.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ stackView.spacing = 6
|
|
|
+ stackView.axis = .horizontal
|
|
|
+ scrollView.addSubview(stackView)
|
|
|
+ stackView.snp.makeConstraints { make in
|
|
|
+ make.edges.equalToSuperview()
|
|
|
+ make.height.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let gradientView = UIView.gradientView([
|
|
|
+ .fill_7.withAlphaComponent(0), .fill_7
|
|
|
+ ], .horizontalLTR)
|
|
|
+ roomSeatsView.addSubview(gradientView)
|
|
|
+ gradientView.snp.makeConstraints { make in
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
+ make.width.equalTo(32)
|
|
|
+ }
|
|
|
+
|
|
|
+ return roomSeatsView
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildSpecifiedUserView() -> UIView {
|
|
|
+
|
|
|
+ return specifiedUserView
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private class LNRoomGiftAvatarView: UIView {
|
|
|
+ private let avatarBg = UIView()
|
|
|
+ private let avatarView = UIImageView()
|
|
|
+ private let badgeBg = UIView()
|
|
|
+ private let badge = UILabel()
|
|
|
+ private(set) var curSeatItem: LNRoomSeatItem?
|
|
|
+
|
|
|
+ var isSelected: Bool = false {
|
|
|
+ didSet {
|
|
|
+ if isSelected {
|
|
|
+ avatarBg.layer.borderColor = UIColor.text_6.cgColor
|
|
|
+ badge.textColor = .text_1
|
|
|
+ badgeBg.backgroundColor = .text_6
|
|
|
+ } else {
|
|
|
+ avatarBg.layer.borderColor = UIColor.clear.cgColor
|
|
|
+ badge.textColor = .text_4
|
|
|
+ badgeBg.backgroundColor = .text_1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func update(_ seat: LNRoomSeatItem) {
|
|
|
+ isHidden = seat.uid.isEmpty || seat.uid.isMyUid
|
|
|
+ avatarView.sd_setImage(with: URL(string: seat.avatar))
|
|
|
+ badge.text = seat.index.giftHeaderTitle
|
|
|
+
|
|
|
+ curSeatItem = seat
|
|
|
+ }
|
|
|
+
|
|
|
+ override init(frame: CGRect) {
|
|
|
+ super.init(frame: frame)
|
|
|
+
|
|
|
+ snp.makeConstraints { make in
|
|
|
+ make.width.height.equalTo(40)
|
|
|
+ }
|
|
|
+
|
|
|
+ avatarBg.backgroundColor = .clear
|
|
|
+ avatarBg.layer.cornerRadius = 17
|
|
|
+ avatarBg.layer.borderWidth = 1
|
|
|
+ avatarBg.layer.borderColor = UIColor.primary_4.cgColor
|
|
|
+ addSubview(avatarBg)
|
|
|
+ avatarBg.snp.makeConstraints { make in
|
|
|
+ make.width.height.equalTo(34)
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
+ make.top.equalToSuperview().offset(1)
|
|
|
+ make.bottom.equalToSuperview().offset(-5)
|
|
|
+ }
|
|
|
+
|
|
|
+ avatarView.layer.cornerRadius = 15
|
|
|
+ avatarView.clipsToBounds = true
|
|
|
+ avatarView.contentMode = .scaleAspectFill
|
|
|
+ avatarBg.addSubview(avatarView)
|
|
|
+ avatarView.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ make.width.height.equalTo(30)
|
|
|
+ }
|
|
|
+
|
|
|
+ badgeBg.backgroundColor = .fill
|
|
|
+ badgeBg.layer.cornerRadius = 6
|
|
|
+ addSubview(badgeBg)
|
|
|
+ badgeBg.snp.makeConstraints { make in
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
+ make.bottom.equalToSuperview().offset(-2)
|
|
|
+ make.height.equalTo(12)
|
|
|
+ make.leading.greaterThanOrEqualToSuperview()
|
|
|
+ make.width.greaterThanOrEqualTo(26)
|
|
|
+ }
|
|
|
+
|
|
|
+ badge.font = .systemFont(ofSize: 10)
|
|
|
+ badge.textColor = .text_4
|
|
|
+ badge.textAlignment = .center
|
|
|
+ badgeBg.addSubview(badge)
|
|
|
+ badge.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(4)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ required init?(coder: NSCoder) {
|
|
|
+ fatalError("init(coder:) has not been implemented")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private class LNRoomGiftSpecifiedUserView: UIView {
|
|
|
+ private let nameLabel = UILabel()
|
|
|
+ private let avatar = UIImageView()
|
|
|
+ private(set) var curUid: String?
|
|
|
+
|
|
|
+ override init(frame: CGRect) {
|
|
|
+ super.init(frame: frame)
|
|
|
+
|
|
|
+ let avatarBg = UIView()
|
|
|
+ avatarBg.backgroundColor = .clear
|
|
|
+ avatarBg.layer.cornerRadius = 17
|
|
|
+ avatarBg.layer.borderWidth = 1
|
|
|
+ avatarBg.layer.borderColor = UIColor.primary_4.cgColor
|
|
|
+ addSubview(avatarBg)
|
|
|
+ avatarBg.snp.makeConstraints { make in
|
|
|
+ make.width.height.equalTo(34)
|
|
|
+ make.leading.equalToSuperview()
|
|
|
+ make.verticalEdges.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ avatar.layer.cornerRadius = 15
|
|
|
+ avatar.clipsToBounds = true
|
|
|
+ avatar.contentMode = .scaleAspectFill
|
|
|
+ avatarBg.addSubview(avatar)
|
|
|
+ avatar.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ make.width.height.equalTo(30)
|
|
|
+ }
|
|
|
+
|
|
|
+ nameLabel.font = .body_s
|
|
|
+ nameLabel.textColor = .text_1
|
|
|
+ addSubview(nameLabel)
|
|
|
+ nameLabel.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalTo(avatarBg.snp.trailing).offset(5)
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func update(_ uid: String?) {
|
|
|
+ curUid = uid
|
|
|
+ guard let uid, !uid.isEmpty else {
|
|
|
+ isHidden = true
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ LNProfileManager.shared.getCachedProfileUserInfo(uid: uid, fetchIfNeeded: true)
|
|
|
+ { [weak self] info in
|
|
|
+ guard let self else { return }
|
|
|
+ guard let info, info.uid == curUid else { return }
|
|
|
+ nameLabel.text = info.name
|
|
|
+ avatar.sd_setImage(with: URL(string: info.avatar))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ required init?(coder: NSCoder) {
|
|
|
+ fatalError("init(coder:) has not been implemented")
|
|
|
+ }
|
|
|
+}
|