Visual_Novel_iOS/crush/Crush/Src/Modules/TestEntrances/TestEntrancesController.swift

517 lines
18 KiB
Swift
Raw Normal View History

2025-10-09 10:29:35 +00:00
//
// TestEntrancesController.swift
// Crush
//
// Created by Leon on 2025/7/12.
//
import UIKit
import Combine
import URLNavigator
class TestEntrance {
let title: String
let destination: UIViewController.Type?
let category: String
let action: (() -> Void)?
init(title: String, destination: UIViewController.Type? = UIViewController.self, action:(() -> Void)? = nil, category: String) {
self.title = title
self.destination = destination
self.category = category
self.action = action
}
}
class TestEntrancesController: CLBaseViewController {
private let tableView = UITableView(frame: .zero, style: .grouped)
private var sections: [String] = []
private var testItems: [String: [TestEntrance]] = [:]
var photoModels: [PhotoBrowserModel] = [PhotoBrowserModel]()
lazy var recordTool = AudioRecordTool()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
configureTestItems()
}
private func setupUI() {
//view.backgroundColor = .white
title = "Test Entrances"
// Setup TableView
tableView.backgroundColor = .clear
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self
tableView.dataSource = self
tableView.register(TestEntranceCell.self, forCellReuseIdentifier: TestEntranceCell.reuseIdentifier)
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
//make.edges.equalToSuperview()
make.leading.trailing.bottom.equalToSuperview()
make.top.equalTo(navigationView.snp.bottom)
}
let headView = TestEntrancesHeadView(frame: CGRect(x: 0, y: 0, width: UIScreen.width, height: 48))
tableView.tableHeaderView = headView
}
private func configureTestItems() {
let common = "Common"
let auth = "Auth"
let role = "Role"
let data = "Data"
let chat = "Chat"
let user = "User"
let discover = "Discover"
let network = "Network"
let ui = "UI"
let customOrder = [common,ui, auth, role, data,chat, user,discover, network]
// Sample test configurations
let items = [
TestEntrance(title: "TapCommon01", action: {[weak self] in self?.tapCommon01()}, category: common),
TestEntrance(title: "TapCommon02", action: {[weak self] in self?.tapCommon02()}, category: common),
TestEntrance(title: "TapCommon03", action: {[weak self] in self?.tapCommon03()}, category: common),
TestEntrance(title: "TapCommon04", action: {[weak self] in self?.tapCommon04()}, category: common),
TestEntrance(title: "TapCommon05 Data", action: {[weak self] in self?.tapCommon05()}, category: common),
TestEntrance(title: "Audio Record and upload", action: {[weak self] in self?.tapCommon06()}, category: common),
TestEntrance(title: "TapCommon07", action: {[weak self] in self?.tapCommon07()}, category: common),
TestEntrance(title: "UI Components", destination: UITestViewController.self, category: ui),
TestEntrance(title: "TestUI1Controller", destination: TestUI1Controller.self, category: ui),
TestEntrance(title: "Login Test", destination: CLLoginMainController.self, category: auth),
TestEntrance(title: "Role Classification", destination: RoleClassificationSelectController.self, category: role),
TestEntrance(title: "Role Voice", destination: RoleVoiceSetController.self, category: role),
TestEntrance(title: "Role Figure Generate", destination: RoleFigureGenerateController.self, category: role),
TestEntrance(title: "Role Home Page", destination: RoleHomePagerController.self, category: role),
TestEntrance(title: "Role Photo Generate", destination: RolePhotoGenerateController.self, category: role),
TestEntrance(title: "Database Test", destination: DatabaseTestViewController.self, category: data),
TestEntrance(title: "IAP Test", destination: DatabaseTestViewController.self, category: data),
TestEntrance(title: "Data Tool", destination: TestDatasTableController.self, category: data),
TestEntrance(title: "Chat Setting", destination: ChatSettingListController.self, category: chat),
TestEntrance(title: "Phone Calling", destination: PhoneCallController.self, category: chat),
TestEntrance(title: "Profile Test", destination: ProfileTestViewController.self, category: user),
TestEntrance(title: "Setting", destination: SettingListController.self, category: user),
TestEntrance(title: "Meet Drag test", destination: MeetDragCardExampleViewController.self, category: discover),
TestEntrance(title: "Network Test", destination: NetworkTestViewController.self, category: network),
]
// Group items by category
testItems = Dictionary(grouping: items, by: { $0.category })
// Get all categories in their original order
let allCategories = items.map { $0.category }.uniqued()
// Sort categories: prioritize customOrder, then append remaining categories in original order
sections = customOrder + allCategories.filter { !customOrder.contains($0) }
tableView.reloadData()
}
}
extension TestEntrancesController{
// Functions
private func tapCommon01(){
// let vc = UnlockPriceSetDialogController()
// vc.show()
// Alert.showAIRoleCreateSuccessAlert()
// let photo = UploadPhotoM()
// photo.image = UIImage(named: "egpic")
// photo.addThisItemTimeStamp = Date().timeStamp
//
// Hud.showIndicator()
// CloudStorage.shared.s3BatchAddPhotos([photo], bucket: .HEAD_IMAGE) { result in
// Hud.hideInidcator()
// dlog("\(result)")
// }
// let color = CLGlobalToken.color(token: .glo_color_violet_10)
// dlog("\(color)")
// let vc = ChatModePickSheet()
// vc.show()
}
private func tapCommon02(){
let sheet = BuyCreditsSheetView()
sheet.show()
}
private func tapCommon03(){
let cropViewController = CropViewController(image: UIImage(named: "egpic")!) { [unowned self] croppedImage in
dlog("crop image: \(croppedImage)")
}
let navc = CLNavigationController(rootViewController: cropViewController)
UIWindow.getTopViewController()?.present(navc, animated: true, completion: nil)
}
private func tapCommon04(){
//var photoModels: [EGPhotoBrowserModel] = [EGPhotoBrowserModel]()
photoModels.removeAll()
do{
let model = PhotoBrowserModel()
// model.image = UIImage(named: "egpic")
model.imageUrl = "https://img2.baidu.com/it/u=3634539496,393899961&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=563"
photoModels.append(model)
}
do{
let model = PhotoBrowserModel()
// model.image = UIImage(named: "egpic")
model.imageUrl = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.nga.178.com%2Fattachments%2Fmon_202011%2F28%2F7nQ5-gg1iZ2hT3cS16p-23w.jpg&refer=http%3A%2F%2Fimg.nga.178.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1756093859&t=f7aa3f1da2fcbda3e44de75aaa5f224a"
photoModels.append(model)
}
ImageBrowser.show(models: photoModels, index: 0, type: .roleMine)
}
private func tapCommon05(){
let mockAIUserRequest = AIUserRequest(
aiId: 123,
nickname: "MockAI",
sex: .female,
headImg: "https://example.com/avatar.jpg",
birthday: "2000-01-01",
roleCode: "mentor",
characterCode: "kind",
tagCode: "funny",
introduction: "I'm a mock AI used for testing.",
permission: 1,
imageUrl: "https://example.com/image.jpg",
imageWidth: 720,
imageHeight: 1280,
aiUserExt: AIUserRequestExt(
profile: "Deep personality profile here",
dialogueStyle: "Friendly",
dialoguePrologue: "Hey there! Nice to meet you.",
dialogueTimbreCode: "warm",
dialoguePitch: "medium",
dialogueSpeechRate: "normal",
imageStyleCode: "realistic",
imageDesc: "A cheerful character in a futuristic setting",
imageReferenceUrl: "https://example.com/reference.jpg"
)
)
let jsonString:String = CodableHelper.encodeToJSONString(mockAIUserRequest)!
dlog("AIUserRequest: \(jsonString)")
let model = CodableHelper.decode(AIUserRequest.self, from: jsonString)
dlog("model: \(String(describing: model))")
}
private func tapCommon06(){
if recordTool.audioRecorder?.isRecording ?? false{
dlog("audio结束录制")
recordTool.stopRecord()
}else{
dlog("audio开始录制")
recordTool.startRecord { path in
if let pathInSandbox = path {
do {
let data = try Data(contentsOf: pathInSandbox)
// data
let model = UploadModel()
model.addThisItemTimeStamp = Date().timeStamp
model.fileData = data
CloudStorage.shared.s3AddUploadAudio(model) { result in
dlog("Audio upload结果\(result)")
}
} catch {
print("❌读取文件audio mp3失败: \(error)")
}
}
}
}
}
private func tapCommon07(){
// AICowProvider.request(.voiceAsr2(url: "https://snd.crushlevel.ai/dev/main/sound/439213911113729/1756776603015.mp3"), modelType: EmptyModel.self) { result in
// switch result {
// case .success(let model):
// dlog(model)
// case .failure:
// break
// }
// }
// let v = CoinsRechargeSheet()
// v.show()
// CLPurchase.shared.showIAPBuyCoinSheet()
// Hud.showIndicator()
}
}
extension Sequence where Element: Hashable {
func uniqued() -> [Element] {
var seen = Set<Element>()
return filter { seen.insert($0).inserted }
}
}
// MARK: - UITableViewDataSource
extension TestEntrancesController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let category = sections[section]
return testItems[category]?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TestEntranceCell.reuseIdentifier, for: indexPath) as! TestEntranceCell
let category = sections[indexPath.section]
if let item = testItems[category]?[indexPath.row] {
cell.configure(with: item)
}
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
}
// MARK: - UITableViewDelegate
extension TestEntrancesController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let category = sections[indexPath.section]
if let item = testItems[category]?[indexPath.row] {
// item.destination is UIViewController.Type
guard let type = item.destination else{
fatalError()
}
if let action = item.action{
action()
return
}
if let baseVCType = type as? CLBaseViewController.Type {
let shouldShow = baseVCType.shouldPresentThisVc
if shouldShow {
let destinationVC = type.init()
let navc = CLNavigationController(rootViewController: destinationVC)
navc.modalPresentationStyle = .fullScreen
present(navc, animated: true)
return
}
}
let destinationVC = type.init()
navigationController?.pushViewController(destinationVC, animated: true)
}
}
}
// MARK: - Custom Cell
class TestEntranceCell: UITableViewCell {
static let reuseIdentifier = "TestEntranceCell"
private let titleLabel = UILabel()
private let categoryLabel = UILabel()
private let arrowLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
backgroundView?.backgroundColor = .clear
backgroundColor = .clear
contentView.backgroundColor = .clear
// Configure labels
titleLabel.font = .systemFont(ofSize: 16, weight: .medium)
titleLabel.textColor = .white
categoryLabel.font = .systemFont(ofSize: 14, weight: .regular)
categoryLabel.textColor = .gray
arrowLabel.text = ""
arrowLabel.textColor = .white
arrowLabel.font = .systemFont(ofSize: 16)
// Setup stack views for three-column layout
let stackView = UIStackView(arrangedSubviews: [categoryLabel, titleLabel, arrowLabel])
stackView.axis = .horizontal
stackView.spacing = 16
stackView.distribution = .fill
stackView.alignment = .center
stackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stackView)
// Constraints
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
categoryLabel.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.3),
titleLabel.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6),
])
}
func configure(with entrance: TestEntrance) {
titleLabel.text = entrance.title
categoryLabel.text = entrance.category
}
}
// MARK: - Sample Test View Controllers
class LoginTestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "Login Test"
}
}
class ProfileTestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "Profile Test"
}
}
class NetworkTestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "Network Test"
}
}
class UITestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "UI Components Test"
}
}
class TestEntrancesHeadView: UIView{
var textFiled: CLTextField!
var operateButton : StyleButton!
private var cancellables = Set<AnyCancellable>()
override init(frame: CGRect) {
super.init(frame: frame)
operateButton = {
let v = StyleButton()
v.primary(size: .small)
v.addTarget(self, action: #selector(tapOperateButton), for: .touchUpInside)
addSubview(v)
v.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-16)
make.centerY.equalToSuperview()
}
v.isEnabled = false
return v
}()
textFiled = {
let v = CLTextField()
v.placeholder = "aiId"
addSubview(v)
v.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(16)
make.centerY.equalToSuperview()
make.trailing.equalTo(operateButton.snp.leading).offset(-16)
}
return v
}()
operateButton.setTitle("进AI主页", for: .normal)
textFiled.textPublisher.sink {[weak self] text in
guard let str = text else{
self?.operateButton.isEnabled = false
return
}
self?.operateButton.isEnabled = str.count > 10
}.store(in: &cancellables)
// aiId
loadCachedAIId()
}
private func setupData(){
textFiled.text = "444190968774657"
textFiled.sendTextChangedNoti()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: -
/// aiId
private func loadCachedAIId() {
if let cachedAIId = AppCache.fetchCache(key: CacheKey.testRecordAIId.rawValue, type: Int.self) {
textFiled.text = "\(cachedAIId)"
textFiled.sendTextChangedNoti()
dlog("✅ 从缓存加载aiId: \(cachedAIId)")
}
}
/// aiId
private func saveAIIdToCache(_ aiId: Int) {
AppCache.cache(key: CacheKey.testRecordAIId.rawValue, value: aiId)
dlog("✅ 已存储aiId到缓存: \(aiId)")
}
@objc private func tapOperateButton(){
guard let text = textFiled.text, let num = Int(text) else{
return
}
guard text.count > 10 else{
return
}
// aiId
saveAIIdToCache(num)
// 437416915828737
AppRouter.goAIRoleHome(aiId: num)
}
}