207 lines
7.9 KiB
Swift
207 lines
7.9 KiB
Swift
//
|
|
// MessageDetailViewController.swift
|
|
// suixinkan
|
|
//
|
|
|
|
import SnapKit
|
|
import UIKit
|
|
|
|
/// 消息详情页,对齐 Android `MsgDetailScreen`。
|
|
final class MessageDetailViewController: BaseViewController {
|
|
var onDeleted: ((Int) -> Void)?
|
|
|
|
private let viewModel: MessageDetailViewModel
|
|
private let api: any MessageCenterServing
|
|
private let travelAlbumAPI: any TravelAlbumServing
|
|
|
|
private let scrollView = UIScrollView()
|
|
private let contentView = UIView()
|
|
private let typeImageView = UIImageView()
|
|
private let titleLabel = UILabel()
|
|
private let timeContainer = UIView()
|
|
private let timeLabel = UILabel()
|
|
private let bodyLabel = UILabel()
|
|
private let taskDetailButton = UIButton(type: .system)
|
|
private let bottomBar = UIView()
|
|
private let deleteButton = UIButton(type: .system)
|
|
|
|
/// 初始化消息详情页。
|
|
init(
|
|
message: MessageItem,
|
|
api: (any MessageCenterServing)? = nil,
|
|
travelAlbumAPI: (any TravelAlbumServing)? = nil
|
|
) {
|
|
viewModel = MessageDetailViewModel(message: message)
|
|
self.api = api ?? NetworkServices.shared.messageCenterAPI
|
|
self.travelAlbumAPI = travelAlbumAPI ?? NetworkServices.shared.travelAlbumAPI
|
|
super.init(nibName: nil, bundle: nil)
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func setupNavigationBar() {
|
|
title = "消息详情"
|
|
}
|
|
|
|
override func setupUI() {
|
|
view.backgroundColor = .white
|
|
|
|
typeImageView.contentMode = .scaleAspectFit
|
|
typeImageView.image = UIImage(named: MessageTypeIcon.assetName(for: viewModel.message.type))
|
|
|
|
titleLabel.text = viewModel.message.displayTitle
|
|
titleLabel.font = .systemFont(ofSize: 20, weight: .bold)
|
|
titleLabel.textColor = .black
|
|
titleLabel.textAlignment = .center
|
|
titleLabel.numberOfLines = 0
|
|
|
|
timeContainer.backgroundColor = UIColor(hex: 0xF4F4F4)
|
|
timeContainer.layer.cornerRadius = 4
|
|
timeContainer.clipsToBounds = true
|
|
|
|
timeLabel.text = viewModel.message.createdAt
|
|
timeLabel.font = .systemFont(ofSize: 12)
|
|
timeLabel.textColor = UIColor(hex: 0x4B5563)
|
|
timeLabel.textAlignment = .center
|
|
timeLabel.numberOfLines = 1
|
|
|
|
bodyLabel.text = viewModel.message.content
|
|
bodyLabel.font = .systemFont(ofSize: 16)
|
|
bodyLabel.textColor = .black
|
|
bodyLabel.numberOfLines = 0
|
|
bodyLabel.textAlignment = .left
|
|
|
|
taskDetailButton.setTitle("查看任务详情", for: .normal)
|
|
taskDetailButton.setTitleColor(.white, for: .normal)
|
|
taskDetailButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
|
|
taskDetailButton.backgroundColor = AppColor.primary
|
|
taskDetailButton.layer.cornerRadius = 10
|
|
taskDetailButton.clipsToBounds = true
|
|
taskDetailButton.isHidden = !viewModel.showsAIRetouchTaskAction
|
|
taskDetailButton.accessibilityLabel = "查看AI修图任务详情"
|
|
|
|
bottomBar.backgroundColor = .white
|
|
deleteButton.setTitle("删除并返回", for: .normal)
|
|
deleteButton.setTitleColor(.white, for: .normal)
|
|
deleteButton.titleLabel?.font = .systemFont(ofSize: 16)
|
|
deleteButton.backgroundColor = UIColor(hex: 0xEF4444)
|
|
deleteButton.layer.cornerRadius = 8
|
|
deleteButton.clipsToBounds = true
|
|
|
|
view.addSubview(scrollView)
|
|
scrollView.addSubview(contentView)
|
|
[typeImageView, titleLabel, timeContainer, bodyLabel, taskDetailButton].forEach(contentView.addSubview)
|
|
timeContainer.addSubview(timeLabel)
|
|
view.addSubview(bottomBar)
|
|
bottomBar.addSubview(deleteButton)
|
|
}
|
|
|
|
override func setupConstraints() {
|
|
bottomBar.snp.makeConstraints { make in
|
|
make.leading.trailing.bottom.equalToSuperview()
|
|
}
|
|
deleteButton.snp.makeConstraints { make in
|
|
make.top.equalToSuperview().offset(16)
|
|
make.leading.trailing.equalToSuperview().inset(32)
|
|
make.height.equalTo(52)
|
|
make.bottom.equalTo(view.safeAreaLayoutGuide).inset(16)
|
|
}
|
|
scrollView.snp.makeConstraints { make in
|
|
make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide)
|
|
make.bottom.equalTo(bottomBar.snp.top)
|
|
}
|
|
contentView.snp.makeConstraints { make in
|
|
make.edges.equalTo(scrollView.contentLayoutGuide)
|
|
make.width.equalTo(scrollView.frameLayoutGuide)
|
|
make.height.greaterThanOrEqualTo(scrollView.frameLayoutGuide)
|
|
}
|
|
typeImageView.snp.makeConstraints { make in
|
|
make.top.equalToSuperview().offset(32)
|
|
make.centerX.equalToSuperview()
|
|
make.size.equalTo(64)
|
|
}
|
|
titleLabel.snp.makeConstraints { make in
|
|
make.top.equalTo(typeImageView.snp.bottom).offset(8)
|
|
make.leading.trailing.equalToSuperview().inset(32)
|
|
}
|
|
timeContainer.snp.makeConstraints { make in
|
|
make.top.equalTo(titleLabel.snp.bottom).offset(8)
|
|
make.centerX.equalToSuperview()
|
|
}
|
|
timeLabel.snp.makeConstraints { make in
|
|
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 2, left: 4, bottom: 2, right: 4))
|
|
}
|
|
bodyLabel.snp.makeConstraints { make in
|
|
make.top.equalTo(timeContainer.snp.bottom).offset(24)
|
|
make.leading.trailing.equalToSuperview().inset(32)
|
|
if !viewModel.showsAIRetouchTaskAction {
|
|
make.bottom.lessThanOrEqualToSuperview().inset(24)
|
|
}
|
|
}
|
|
if viewModel.showsAIRetouchTaskAction {
|
|
taskDetailButton.snp.makeConstraints { make in
|
|
make.top.equalTo(bodyLabel.snp.bottom).offset(24)
|
|
make.leading.trailing.equalToSuperview().inset(32)
|
|
make.height.equalTo(52)
|
|
make.bottom.lessThanOrEqualToSuperview().inset(24)
|
|
}
|
|
}
|
|
}
|
|
|
|
override func bindActions() {
|
|
deleteButton.addTarget(self, action: #selector(deleteTapped), for: .touchUpInside)
|
|
taskDetailButton.addTarget(self, action: #selector(taskDetailTapped), for: .touchUpInside)
|
|
viewModel.onStateChange = { [weak self] in
|
|
Task { @MainActor in
|
|
self?.applyState()
|
|
}
|
|
}
|
|
viewModel.onShowMessage = { [weak self] message in
|
|
Task { @MainActor in
|
|
self?.showToast(message)
|
|
}
|
|
}
|
|
viewModel.onDeleted = { [weak self] id in
|
|
Task { @MainActor in
|
|
self?.onDeleted?(id)
|
|
self?.navigationController?.popViewController(animated: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc private func taskDetailTapped() {
|
|
let target: UIViewController
|
|
if let batchId = viewModel.aiRetouchBatchId {
|
|
target = TravelAlbumAIJobDetailViewController(batchId: batchId, api: travelAlbumAPI)
|
|
} else {
|
|
target = TravelAlbumAIJobListViewController(api: travelAlbumAPI)
|
|
}
|
|
navigationController?.pushViewController(target, animated: true)
|
|
}
|
|
|
|
@objc private func deleteTapped() {
|
|
let alert = UIAlertController(title: "删除消息", message: "确定要删除这条消息吗?", preferredStyle: .alert)
|
|
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
|
|
alert.addAction(UIAlertAction(title: "确认", style: .destructive) { [weak self] _ in
|
|
guard let self else { return }
|
|
Task { @MainActor in
|
|
await self.viewModel.delete(api: self.api)
|
|
}
|
|
})
|
|
present(alert, animated: true)
|
|
}
|
|
|
|
private func applyState() {
|
|
deleteButton.isEnabled = !viewModel.isDeleting
|
|
deleteButton.alpha = viewModel.isDeleting ? 0.65 : 1
|
|
if viewModel.isDeleting {
|
|
showLoading()
|
|
} else {
|
|
hideLoading()
|
|
}
|
|
}
|
|
}
|