Path: blob/a-new-beginning/Folium-iOS/Classes/Cells/CytrusMultplayerRoomCell.swift
2 views
//
// CytrusMultplayerRoomCell.swift
// Folium-iOS
//
// Created by Jarrod Norwell on 21/10/2025.
//
import Cytrus
import Foundation
import UIKit
class CytrusMultplayerRoomCell : UICollectionViewCell {
var textLabel: UILabel? = nil, // ip
secondaryTextLabel: UILabel? = nil, // name
tertiaryTextLabel: UILabel? = nil // details
var playersLabel: UILabel? = nil,
maxPlayersTextLabel: UILabel? = nil
var optionsButton: UIButton? = nil
var imageView: UIImageView? = nil
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .secondarySystemBackground
textLabel = .init()
guard let textLabel else {
return
}
textLabel.translatesAutoresizingMaskIntoConstraints = false
textLabel.font = .preferredFont(forTextStyle: .headline)
textLabel.textAlignment = .left
textLabel.textColor = .tertiaryLabel
addSubview(textLabel)
secondaryTextLabel = .init()
guard let secondaryTextLabel else {
return
}
secondaryTextLabel.translatesAutoresizingMaskIntoConstraints = false
secondaryTextLabel.font = .bold(.title2)
secondaryTextLabel.numberOfLines = 3
secondaryTextLabel.textAlignment = .left
secondaryTextLabel.textColor = .label
addSubview(secondaryTextLabel)
tertiaryTextLabel = .init()
guard let tertiaryTextLabel else {
return
}
tertiaryTextLabel.translatesAutoresizingMaskIntoConstraints = false
tertiaryTextLabel.font = .preferredFont(forTextStyle: .body)
tertiaryTextLabel.numberOfLines = 3
tertiaryTextLabel.textAlignment = .left
tertiaryTextLabel.textColor = .secondaryLabel
addSubview(tertiaryTextLabel)
maxPlayersTextLabel = .init()
guard let maxPlayersTextLabel else {
return
}
maxPlayersTextLabel.translatesAutoresizingMaskIntoConstraints = false
maxPlayersTextLabel.font = .bold(.body)
maxPlayersTextLabel.textAlignment = .left
maxPlayersTextLabel.textColor = .tertiaryLabel
addSubview(maxPlayersTextLabel)
playersLabel = .init()
guard let playersLabel else {
return
}
playersLabel.translatesAutoresizingMaskIntoConstraints = false
playersLabel.font = .bold(.body)
playersLabel.textAlignment = .left
playersLabel.textColor = .tertiaryLabel
addSubview(playersLabel)
var configuration: UIButton.Configuration = if #available(iOS 26, *) {
.glass()
} else {
.filled()
}
configuration.buttonSize = .small
configuration.cornerStyle = .capsule
configuration.image = .init(systemName: "ellipsis")
optionsButton = .init(configuration: configuration)
guard let optionsButton else {
return
}
optionsButton.translatesAutoresizingMaskIntoConstraints = false
optionsButton.showsMenuAsPrimaryAction = true
if #unavailable(iOS 26) {
optionsButton.layer.shadowColor = UIColor.black.cgColor
optionsButton.layer.shadowOpacity = 1 / 4
optionsButton.layer.shadowRadius = 20
optionsButton.layer.shadowOffset = .init(width: 0, height: 10)
}
optionsButton.alpha = 0
addSubview(optionsButton)
imageView = .init()
guard let imageView else {
return
}
imageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imageView)
addConstraints([
textLabel.topAnchor.constraint(equalTo: topAnchor, constant: 20),
textLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
textLabel.trailingAnchor.constraint(lessThanOrEqualTo: imageView.safeAreaLayoutGuide.leadingAnchor, constant: -20),
secondaryTextLabel.topAnchor.constraint(equalTo: textLabel.safeAreaLayoutGuide.bottomAnchor, constant: 8),
secondaryTextLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
secondaryTextLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -20),
tertiaryTextLabel.topAnchor.constraint(equalTo: secondaryTextLabel.safeAreaLayoutGuide.bottomAnchor, constant: 8),
tertiaryTextLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
tertiaryTextLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -20),
playersLabel.topAnchor.constraint(equalTo: tertiaryTextLabel.safeAreaLayoutGuide.bottomAnchor, constant: 8),
playersLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
maxPlayersTextLabel.topAnchor.constraint(equalTo: tertiaryTextLabel.safeAreaLayoutGuide.bottomAnchor, constant: 8),
maxPlayersTextLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 20),
maxPlayersTextLabel.trailingAnchor.constraint(equalTo: playersLabel.safeAreaLayoutGuide.leadingAnchor, constant: -20),
maxPlayersTextLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
optionsButton.topAnchor.constraint(equalTo: topAnchor, constant: 20),
optionsButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
imageView.topAnchor.constraint(equalTo: topAnchor, constant: 20),
imageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
guard let optionsButton, let configuration = optionsButton.configuration else {
return
}
layer.cornerRadius = configuration.background.cornerRadius + 20
}
private func attributed(_ font: UIFont, _ string: String, _ image: UIImage? = nil) -> AttributedString {
var secondaryAttributedText: AttributedString = .init()
let attachment: NSTextAttachment = .init()
attachment.image = image?.withTintColor(.tertiaryLabel)
.applyingSymbolConfiguration(.init(scale: .small))
let attributed: NSAttributedString = .init(attachment: attachment)
if let converted: AttributedString = try? .init(attributed, including: \.uiKit) {
secondaryAttributedText.append(converted)
}
secondaryAttributedText.append(AttributedString(string))
return secondaryAttributedText
}
func set(_ room: CytrusRoom, isCurrent: Bool = false) {
guard let textLabel, let secondaryTextLabel, let tertiaryTextLabel,
let maxPlayersTextLabel, let playersLabel, let imageView else {
return
}
textLabel.text = room.ip
secondaryTextLabel.text = room.name
tertiaryTextLabel.text = room.details.trimmingCharacters(in: .whitespaces).isEmpty ? "No Details" : room.details
maxPlayersTextLabel.attributedText = .init(attributed(.bold(.body),
" \(room.maximumPlayers) maximum",
.init(systemName: "number.circle.fill")))
playersLabel.attributedText = .init(attributed(.bold(.body),
" \(room.numberOfPlayers) current",
.init(systemName: "person.crop.circle.fill")))
imageView.image = .init(systemName: isCurrent ? "checkmark" : room.passwordLocked ? "lock.fill" : "lock.open.fill")?
.applyingSymbolConfiguration(.init(scale: .medium))
}
}