211 lines
6.6 KiB
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)
|
|
}
|
|
}
|