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

517 lines
18 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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)
}
}