完善景区排队设置页与全局按钮配置迁移。

重构排队设置与变更日志交互,补充 Overlay 与资源,并将多页面主操作按钮统一到 UIButton Configuration,同步更新相关单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-14 16:51:23 +08:00
parent 6336feff27
commit 5eef31b8da
17 changed files with 181 additions and 124 deletions

View File

@ -554,7 +554,17 @@ private final class MediaPreviewPlaybackButton: UIButton {
private func setupUI() {
tintColor = .white
backgroundColor = UIColor.white.withAlphaComponent(0.08)
adjustsImageWhenHighlighted = false
var buttonConfiguration = UIButton.Configuration.plain()
buttonConfiguration.baseForegroundColor = .white
buttonConfiguration.contentInsets = .zero
configuration = buttonConfiguration
configurationUpdateHandler = { button in
var updatedConfiguration = button.configuration
updatedConfiguration?.baseForegroundColor = .white
button.configuration = updatedConfiguration
button.alpha = 1
button.imageView?.alpha = 1
}
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.28

View File

@ -101,8 +101,8 @@ final class CooperationAcquirerViewController: BaseViewController, UITableViewDa
case .authorized:
presentScanner()
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
Task { @MainActor in
AVCaptureDevice.requestAccess(for: .video) { granted in
Task { @MainActor [weak self] in
if granted {
self?.presentScanner()
}

View File

@ -358,28 +358,27 @@ final class MaterialFormViewController: BaseViewController {
}
private func makeVideoItem(data: Data, fileName: String, sourceURL: URL) -> MaterialUploadedMediaItem {
let size = videoDisplaySize(url: sourceURL)
let preview = videoPreview(url: sourceURL)
return MaterialUploadedMediaItem(
data: data,
thumbnailData: videoThumbnailData(url: sourceURL),
thumbnailData: preview.thumbnailData,
fileName: fileName,
width: Int(size.width),
height: Int(size.height)
width: preview.width,
height: preview.height
)
}
private func videoDisplaySize(url: URL) -> CGSize {
let asset = AVAsset(url: url)
guard let track = asset.tracks(withMediaType: .video).first else { return .zero }
let transformed = track.naturalSize.applying(track.preferredTransform)
return CGSize(width: abs(transformed.width), height: abs(transformed.height))
}
private func videoThumbnailData(url: URL) -> Data? {
private func videoPreview(url: URL) -> (thumbnailData: Data?, width: Int, height: Int) {
let generator = AVAssetImageGenerator(asset: AVAsset(url: url))
generator.appliesPreferredTrackTransform = true
guard let imageRef = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return nil }
return UIImage(cgImage: imageRef).jpegData(compressionQuality: 0.75)
guard let imageRef = try? generator.copyCGImage(at: .zero, actualTime: nil) else {
return (nil, 0, 0)
}
return (
UIImage(cgImage: imageRef).jpegData(compressionQuality: 0.75),
imageRef.width,
imageRef.height
)
}
}

View File

@ -1032,7 +1032,7 @@ final class ProfileAddScheduleViewController: BaseViewController {
api: profileAPI
)
if success {
await MainActor.run { navigationController?.popViewController(animated: true) }
_ = await MainActor.run { navigationController?.popViewController(animated: true) }
}
}
}

View File

@ -417,28 +417,27 @@ final class UploadSampleViewController: SampleBaseViewController {
}
private func makeVideoItem(data: Data, fileName: String, sourceURL: URL) -> SampleUploadedMediaItem {
let size = videoDisplaySize(url: sourceURL)
let preview = videoPreview(url: sourceURL)
return SampleUploadedMediaItem(
data: data,
thumbnailData: videoThumbnailData(url: sourceURL),
thumbnailData: preview.thumbnailData,
fileName: fileName,
width: Int(size.width),
height: Int(size.height)
width: preview.width,
height: preview.height
)
}
private func videoDisplaySize(url: URL) -> CGSize {
let asset = AVAsset(url: url)
guard let track = asset.tracks(withMediaType: .video).first else { return .zero }
let transformed = track.naturalSize.applying(track.preferredTransform)
return CGSize(width: abs(transformed.width), height: abs(transformed.height))
}
private func videoThumbnailData(url: URL) -> Data? {
private func videoPreview(url: URL) -> (thumbnailData: Data?, width: Int, height: Int) {
let generator = AVAssetImageGenerator(asset: AVAsset(url: url))
generator.appliesPreferredTrackTransform = true
guard let imageRef = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return nil }
return UIImage(cgImage: imageRef).jpegData(compressionQuality: 0.75)
guard let imageRef = try? generator.copyCGImage(at: .zero, actualTime: nil) else {
return (nil, 0, 0)
}
return (
UIImage(cgImage: imageRef).jpegData(compressionQuality: 0.75),
imageRef.width,
imageRef.height
)
}
}

View File

@ -469,7 +469,9 @@ final class ScenicQueueSettingsViewController: BaseViewController {
button.backgroundColor = ScenicQueueTokens.bannerBlue
button.layer.cornerRadius = ScenicQueueTokens.radiusInput
button.addTarget(self, action: action, for: .touchUpInside)
button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
button.setConfigurationContentInsets(
NSDirectionalEdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
)
return button
}

View File

@ -317,7 +317,7 @@ final class TaskAddViewController: BaseViewController {
let uploadTaskId = UUID().uuidString.replacingOccurrences(of: "-", with: "")
viewModel.addUploadPlaceholder(
fileName: TaskMediaLoader.suggestedFileName(
from: result.itemProvider,
from: result.itemProvider.suggestedName,
fallback: "upload_\(uploadTaskId).\(isImage ? "jpg" : "mp4")"
),
fileType: isImage ? 2 : 1,
@ -472,8 +472,8 @@ enum TaskMediaLoader {
}
/// 使
static func suggestedFileName(from provider: NSItemProvider, fallback: String) -> String {
guard let suggestedName = provider.suggestedName?.trimmingCharacters(in: .whitespacesAndNewlines),
static func suggestedFileName(from suggestedFileName: String?, fallback: String) -> String {
guard let suggestedName = suggestedFileName?.trimmingCharacters(in: .whitespacesAndNewlines),
!suggestedName.isEmpty else {
return fallback
}
@ -493,7 +493,8 @@ enum TaskMediaLoader {
}
private static func loadImage(from provider: NSItemProvider) async throws -> Payload {
try await withCheckedThrowingContinuation { continuation in
let suggestedName = provider.suggestedName
return try await withCheckedThrowingContinuation { continuation in
let identifier = provider.registeredTypeIdentifiers.first {
UTType($0)?.conforms(to: .image) == true
} ?? UTType.image.identifier
@ -508,7 +509,7 @@ enum TaskMediaLoader {
}
let ext = url.pathExtension.isEmpty ? "jpg" : url.pathExtension
let fallback = "image_\(UUID().uuidString).\(ext)"
let fileName = suggestedFileName(from: provider, fallback: fallback)
let fileName = suggestedFileName(from: suggestedName, fallback: fallback)
let previewData = UIImage(data: data).flatMap(makePreviewData(from:))
continuation.resume(returning: Payload(data: data, fileName: fileName, previewData: previewData))
}
@ -517,6 +518,7 @@ enum TaskMediaLoader {
private static func loadVideo(from provider: NSItemProvider) async throws -> Payload {
let typeIdentifier = UTType.movie.identifier
let suggestedName = provider.suggestedName
return try await withCheckedThrowingContinuation { continuation in
provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { url, error in
if let error {
@ -529,7 +531,7 @@ enum TaskMediaLoader {
}
let ext = url.pathExtension.isEmpty ? "mp4" : url.pathExtension
let fallback = "video_\(UUID().uuidString).\(ext)"
let fileName = suggestedFileName(from: provider, fallback: fallback)
let fileName = suggestedFileName(from: suggestedName, fallback: fallback)
continuation.resume(returning: Payload(
data: data,
fileName: fileName,

View File

@ -858,6 +858,11 @@ extension WiredCameraTransferViewController: PHPickerViewControllerDelegate {
) async -> TravelAlbumPhoneAlbumImportItem? {
let provider = result.itemProvider
let typeIdentifier = preferredImageTypeIdentifier(from: provider)
let fileName = importFileName(
suggestedName: provider.suggestedName,
typeIdentifier: typeIdentifier,
index: index
)
return await withCheckedContinuation { continuation in
provider.loadDataRepresentation(forTypeIdentifier: typeIdentifier) { data, _ in
guard let data, !data.isEmpty else {
@ -867,7 +872,7 @@ extension WiredCameraTransferViewController: PHPickerViewControllerDelegate {
continuation.resume(
returning: TravelAlbumPhoneAlbumImportItem(
data: data,
fileName: importFileName(provider: provider, typeIdentifier: typeIdentifier, index: index),
fileName: fileName,
createdAt: Date()
)
)
@ -881,9 +886,9 @@ extension WiredCameraTransferViewController: PHPickerViewControllerDelegate {
} ?? UTType.image.identifier
}
private static func importFileName(provider: NSItemProvider, typeIdentifier: String, index: Int) -> String {
private static func importFileName(suggestedName: String?, typeIdentifier: String, index: Int) -> String {
let fallback = "album_image_\(Int(Date().timeIntervalSince1970))_\(index)"
let rawName = provider.suggestedName?.trimmingCharacters(in: .whitespacesAndNewlines)
let rawName = suggestedName?.trimmingCharacters(in: .whitespacesAndNewlines)
let baseName = rawName?.isEmpty == false ? rawName! : fallback
guard URL(fileURLWithPath: baseName).pathExtension.isEmpty else {
return baseName