// // LNJoinUsVoiceExamplePanel.swift // Gami // // Created by OneeChan on 2026/1/22. // import Foundation import UIKit import SnapKit class LNJoinUsVoiceExamplePanel: LNPopupView { private let titleLabel = UILabel() private let descLabel = UILabel() private let playIcon = UIImageView() private let voiceWaveView = LNVoiceWaveView() private let playDurationLabel = UILabel() private var curItem: LNSkillFieldExample? override init(frame: CGRect) { super.init(frame: frame) setupViews() LNEventDeliver.addObserver(self) } func update(_ example: LNSkillFieldExample) { curItem = example updateDuration() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNJoinUsVoiceExamplePanel: LNVoicePlayerNotify { func onAudioUpdateDuration(path: String, cur: TimeInterval, total: TimeInterval) { guard curItem?.value == path else { return } let remain = Int(total - cur) playDurationLabel.text = "\(remain)“" } func onAudioStopPlay(path: String) { guard curItem?.value == path else { return } updateDuration() playIcon.image = .icVoicePlay voiceWaveView.stopAnimate() } func onAudioStartPlay(path: String) { guard curItem?.value == path else { return } playIcon.image = .icVoicePause voiceWaveView.startAnimate() } } extension LNJoinUsVoiceExamplePanel { private func updateDuration() { guard let url = curItem?.value else { return } LNVoiceResourceManager.shared.getRemoteAudioDuration(urlStr: url) { [weak self] duration, _ in guard let self else { return } guard let duration else { return } let intDuration = Int(duration) playDurationLabel.text = "\(intDuration)“" } } private func setupViews() { let headerView = buildHeader() container.addSubview(headerView) headerView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(32) make.top.equalToSuperview() } let playView = buildPlayView() container.addSubview(playView) playView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalTo(headerView.snp.bottom) make.bottom.equalToSuperview().offset(commonBottomInset) } } private func buildHeader() -> UIView { let container = UIView() titleLabel.font = .heading_h3 titleLabel.textColor = .text_5 titleLabel.numberOfLines = 0 titleLabel.textAlignment = .center container.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(32) make.top.equalToSuperview().offset(13) } descLabel.font = .body_xs descLabel.textColor = .text_3 descLabel.numberOfLines = 0 descLabel.textAlignment = .center container.addSubview(descLabel) descLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(32) make.bottom.equalToSuperview().offset(-13) make.top.equalTo(titleLabel.snp.bottom).offset(2) } return container } private func buildPlayView() -> UIView { let playView = UIView() let button = UIButton() button.setBackgroundImage(.primary_7, for: .normal) button.layer.cornerRadius = 16 button.clipsToBounds = true button.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } if LNVoicePlayer.shared.isPlaying { LNVoicePlayer.shared.stop() } else if let url = curItem?.value { LNVoicePlayer.shared.play(url) } }), for: .touchUpInside) playView.addSubview(button) button.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalToSuperview().offset(20) make.bottom.equalToSuperview().offset(-50) make.width.equalTo(163) make.height.equalTo(32) } playIcon.image = .icVoicePlay button.addSubview(playIcon) playIcon.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalToSuperview().offset(3) make.width.height.equalTo(22) } voiceWaveView.build() button.addSubview(voiceWaveView) voiceWaveView.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(playIcon.snp.trailing).offset(7) make.width.equalTo(18) make.height.equalTo(11) } playDurationLabel.font = .heading_h5 playDurationLabel.textColor = .text_1 button.addSubview(playDurationLabel) playDurationLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-8) } return playView } }