feat: 添加云盘与消息中心功能

This commit is contained in:
2026-07-09 22:37:43 +08:00
parent 8e356973bd
commit f20ec7f06c
91 changed files with 5437 additions and 89 deletions

View File

@ -0,0 +1,189 @@
//
// CloudDriveFileCell.swift
// suixinkan
//
import Kingfisher
import SnapKit
import UIKit
/// Cell Android
final class CloudDriveFileCell: UICollectionViewCell {
static let reuseIdentifier = "CloudDriveFileCell"
var onDetails: (() -> Void)?
private let thumbnailView = UIImageView()
private let folderIconView = UIImageView(image: CloudDriveAsset.image(named: "icon_cloud_storage_folder_type", fallbackSystemName: "folder.fill"))
private let playIconView = UIImageView(image: CloudDriveAsset.image(named: "icon_cloud_storage_file_play", fallbackSystemName: "play.circle.fill"))
private let extensionLabel = CloudDrivePaddingLabel()
private let nameLabel = UILabel()
private let detailLabel = UILabel()
private let moreButton = UIButton(type: .system)
private var gridConstraints: [Constraint] = []
private var listConstraints: [Constraint] = []
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
setupConstraints()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
thumbnailView.kf.cancelDownloadTask()
thumbnailView.image = nil
folderIconView.isHidden = true
playIconView.isHidden = true
extensionLabel.isHidden = true
onDetails = nil
}
///
func apply(file: CloudFile, mode: CloudDriveDisplayMode) {
nameLabel.text = file.name
detailLabel.text = mode == .grid ? CloudDriveFormatter.typeName(file.type) : CloudDriveFormatter.fileDescription(file)
configureThumbnail(file)
gridConstraints.forEach { mode == .grid ? $0.activate() : $0.deactivate() }
listConstraints.forEach { mode == .list ? $0.activate() : $0.deactivate() }
contentView.layer.cornerRadius = mode == .grid ? AppRadius.lg : 0
contentView.layer.borderWidth = mode == .grid ? 1 : 0
contentView.backgroundColor = mode == .grid ? .white : .clear
thumbnailView.layer.cornerRadius = mode == .grid ? AppRadius.lg : 0
nameLabel.textAlignment = mode == .grid ? .left : .left
}
private func setupUI() {
contentView.clipsToBounds = true
contentView.layer.borderColor = UIColor(hex: 0xF4F4F4).cgColor
thumbnailView.contentMode = .scaleAspectFill
thumbnailView.clipsToBounds = true
thumbnailView.backgroundColor = AppColor.warningBackground
folderIconView.tintColor = AppColor.warning
folderIconView.contentMode = .scaleAspectFit
playIconView.tintColor = .white
playIconView.contentMode = .scaleAspectFit
playIconView.isHidden = true
extensionLabel.font = .app(.caption)
extensionLabel.textColor = .white
extensionLabel.backgroundColor = UIColor.black.withAlphaComponent(0.5)
extensionLabel.layer.cornerRadius = AppRadius.xs
extensionLabel.clipsToBounds = true
extensionLabel.isHidden = true
nameLabel.font = .app(.body)
nameLabel.textColor = AppColor.textPrimary
nameLabel.numberOfLines = 1
detailLabel.font = .app(.caption)
detailLabel.textColor = UIColor(hex: 0x4B5563)
detailLabel.numberOfLines = 1
moreButton.setImage(CloudDriveAsset.image(named: "icon_cloud_storage_file_details", fallbackSystemName: "ellipsis"), for: .normal)
moreButton.tintColor = AppColor.textSecondary
moreButton.addTarget(self, action: #selector(moreTapped), for: .touchUpInside)
contentView.addSubview(thumbnailView)
contentView.addSubview(folderIconView)
contentView.addSubview(playIconView)
contentView.addSubview(extensionLabel)
contentView.addSubview(nameLabel)
contentView.addSubview(detailLabel)
contentView.addSubview(moreButton)
}
private func setupConstraints() {
thumbnailView.snp.makeConstraints { make in
gridConstraints.append(make.top.leading.trailing.equalToSuperview().constraint)
gridConstraints.append(make.height.equalTo(thumbnailView.snp.width).multipliedBy(0.56).constraint)
listConstraints.append(make.leading.centerY.equalToSuperview().constraint)
listConstraints.append(make.width.height.equalTo(48).constraint)
}
folderIconView.snp.makeConstraints { make in
make.center.equalTo(thumbnailView)
make.width.height.equalTo(42)
}
playIconView.snp.makeConstraints { make in
make.center.equalTo(thumbnailView)
make.width.height.equalTo(28)
}
extensionLabel.snp.makeConstraints { make in
make.top.leading.equalTo(thumbnailView).offset(AppSpacing.xs)
}
nameLabel.snp.makeConstraints { make in
gridConstraints.append(make.top.equalTo(thumbnailView.snp.bottom).offset(AppSpacing.xs).constraint)
gridConstraints.append(make.leading.equalToSuperview().offset(AppSpacing.sm).constraint)
listConstraints.append(make.top.equalToSuperview().offset(AppSpacing.sm).constraint)
listConstraints.append(make.leading.equalTo(thumbnailView.snp.trailing).offset(AppSpacing.sm).constraint)
make.trailing.equalTo(moreButton.snp.leading).offset(-AppSpacing.xs)
}
detailLabel.snp.makeConstraints { make in
make.top.equalTo(nameLabel.snp.bottom).offset(AppSpacing.xxs)
make.leading.equalTo(nameLabel)
make.trailing.equalTo(nameLabel)
}
moreButton.snp.makeConstraints { make in
make.trailing.equalToSuperview().inset(AppSpacing.xs)
make.centerY.equalTo(nameLabel.snp.bottom).offset(4)
make.width.height.equalTo(32)
}
listConstraints.forEach { $0.deactivate() }
}
private func configureThumbnail(_ file: CloudFile) {
if file.isFolder {
thumbnailView.image = nil
thumbnailView.backgroundColor = AppColor.warningBackground
folderIconView.isHidden = false
extensionLabel.isHidden = true
return
}
folderIconView.isHidden = true
thumbnailView.backgroundColor = AppColor.inputBackground
let preview = file.isVideo ? file.coverUrl : file.fileUrl
if let url = URL(string: preview), !preview.isEmpty {
let placeholder = CloudDriveAsset.image(
named: file.isVideo ? "icon_cloud_storage_file_type_video" : "icon_cloud_storage_file_type_photo",
fallbackSystemName: file.isVideo ? "video" : "photo"
)
thumbnailView.kf.setImage(with: url, placeholder: placeholder)
} else {
thumbnailView.image = CloudDriveAsset.image(
named: file.isVideo ? "icon_cloud_storage_file_type_video" : "icon_cloud_storage_file_type_photo",
fallbackSystemName: file.isVideo ? "video" : "photo"
)
thumbnailView.tintColor = AppColor.textTertiary
}
playIconView.isHidden = !file.isVideo
extensionLabel.text = CloudDriveFormatter.fileExtension(file.fileUrl.isEmpty ? file.name : file.fileUrl)
extensionLabel.isHidden = false
}
@objc private func moreTapped() {
onDetails?()
}
}
///
private final class CloudDrivePaddingLabel: UILabel {
var insets = UIEdgeInsets(top: 3, left: 5, bottom: 3, right: 5)
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + insets.left + insets.right, height: size.height + insets.top + insets.bottom)
}
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: insets))
}
}