jsdw_ios/QuickLocation/Section/PigeonMessage/PigeonMessageHistoryVC.swift

211 lines
6.6 KiB
Swift

//
// PigeonMessageHistoryVC.swift
// QuickLocation
//
import UIKit
final class PigeonMessageHistoryVC: BaseViewController {
private let members: [GroupMemberModel]
private var items: [PigeonHistoryItem] = []
private var rootView: PigeonMessageHistoryView!
private var playingItemId: String?
private var isVoicePlaying = false
private var voiceElapsed: TimeInterval = 0
private var voiceDuration: TimeInterval = 10
private var voiceTimer: Timer?
init(members: [GroupMemberModel]) {
self.members = members
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
rootView = PigeonMessageHistoryView(frame: UIScreen.main.bounds)
view = rootView
}
override func viewDidLoad() {
super.viewDidLoad()
items = Self.makeMockItems(members: members)
rootView.tableView.dataSource = self
rootView.tableView.delegate = self
rootView.tableView.register(PigeonHistoryCell.self, forCellReuseIdentifier: PigeonHistoryCell.reuseId)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopVoicePlayback(reset: true)
}
@objc private func goBack() {
navigationController?.popViewController(animated: true)
}
private func togglePlayback(for item: PigeonHistoryItem) {
guard item.kind == .voice else { return }
if playingItemId == item.id {
if isVoicePlaying {
pauseVoicePlayback()
} else {
startVoicePlayback(item: item)
}
return
}
stopVoicePlayback(reset: true)
startVoicePlayback(item: item)
}
private func startVoicePlayback(item: PigeonHistoryItem) {
playingItemId = item.id
voiceDuration = max(1, item.duration)
isVoicePlaying = true
voiceTimer?.invalidate()
let timer = Timer(timeInterval: 0.05, repeats: true) { [weak self] _ in
self?.tickVoicePlayback()
}
RunLoop.main.add(timer, forMode: .common)
voiceTimer = timer
refreshVisiblePlayback()
}
private func pauseVoicePlayback() {
isVoicePlaying = false
voiceTimer?.invalidate()
voiceTimer = nil
refreshVisiblePlayback()
}
private func stopVoicePlayback(reset: Bool) {
isVoicePlaying = false
voiceTimer?.invalidate()
voiceTimer = nil
if reset {
voiceElapsed = 0
playingItemId = nil
}
refreshVisiblePlayback()
}
private func tickVoicePlayback() {
voiceElapsed = min(voiceDuration, voiceElapsed + 0.05)
refreshVisiblePlayback()
guard voiceElapsed >= voiceDuration else { return }
stopVoicePlayback(reset: true)
}
private func refreshVisiblePlayback() {
for cell in rootView.tableView.visibleCells {
guard let historyCell = cell as? PigeonHistoryCell,
let indexPath = rootView.tableView.indexPath(for: historyCell) else {
continue
}
let item = items[indexPath.row]
let isCurrent = item.id == playingItemId
historyCell.applyPlayback(
isPlaying: isCurrent && isVoicePlaying,
elapsed: isCurrent ? voiceElapsed : 0,
duration: item.duration
)
}
}
private static func makeMockItems(members: [GroupMemberModel]) -> [PigeonHistoryItem] {
let fallback = UIImage(named: "UserIcon/1")
let sender = AppContextManager.shared.avaterIcon.size == .zero
? fallback
: AppContextManager.shared.avaterIcon
var receivers = members
.filter { $0.user_id != AppContextManager.shared.userId }
.map { member -> UIImage in
member.userIcon.size == .zero ? (fallback ?? UIImage()) : member.userIcon
}
if receivers.isEmpty {
receivers = [fallback ?? UIImage()]
}
while receivers.count < 3 {
receivers.append(receivers[receivers.count % max(receivers.count, 1)])
}
let dateText = "8.14 周五"
let timeText = "下午 10:12"
let caption = "和红红火火恍恍惚惚或或或或或或或或"
return [
PigeonHistoryItem(
id: "image-1",
dateText: dateText,
timeText: timeText,
kind: .image,
caption: caption,
image: UIImage(named: "PigeonMessage/history_photo") ?? sender ?? fallback,
senderAvatar: sender,
receiverAvatars: [receivers[0]],
duration: 0
),
PigeonHistoryItem(
id: "voice-1",
dateText: dateText,
timeText: timeText,
kind: .voice,
caption: "",
image: nil,
senderAvatar: sender,
receiverAvatars: [receivers[0]],
duration: 10
),
PigeonHistoryItem(
id: "voice-2",
dateText: dateText,
timeText: timeText,
kind: .voice,
caption: "",
image: nil,
senderAvatar: sender,
receiverAvatars: Array(receivers.prefix(3)),
duration: 10
)
]
}
}
extension PigeonMessageHistoryVC: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: PigeonHistoryCell.reuseId,
for: indexPath
) as! PigeonHistoryCell
let item = items[indexPath.row]
let isCurrent = item.id == playingItemId
cell.configure(
item: item,
isPlaying: isCurrent && isVoicePlaying,
elapsed: isCurrent ? voiceElapsed : 0
)
cell.onTogglePlay = { [weak self] in
self?.togglePlayback(for: item)
}
return cell
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
guard isVoicePlaying else { return }
stopVoicePlayback(reset: true)
}
}