517 lines
18 KiB
Swift
517 lines
18 KiB
Swift
//
|
||
// 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)
|
||
}
|
||
}
|