407 lines
15 KiB
Swift
407 lines
15 KiB
Swift
//
|
||
// RolePublishController.swift
|
||
// Crush
|
||
//
|
||
// Created by Leon on 2025/7/20.
|
||
//
|
||
|
||
import UIKit
|
||
import Combine
|
||
import TZImagePickerController
|
||
|
||
/// 生成角色页面
|
||
/// 选参考图
|
||
class RolePublishController: RoleCreateBaseController<RolePublishView> {
|
||
weak var viewModel: RoleCreateViewModel!
|
||
|
||
// @Published var image: UIImage?
|
||
private var cancellables = Set<AnyCancellable>()
|
||
|
||
var isAllImagesOK: Bool = false
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
|
||
// Do any additional setup after loading the view.
|
||
setupViews()
|
||
setupDatas()
|
||
setupEvents()
|
||
}
|
||
|
||
override func viewWillAppear(_ animated: Bool) {
|
||
super.viewWillAppear(animated)
|
||
disabledFullScreenPan()
|
||
}
|
||
|
||
override func viewWillDisappear(_ animated: Bool) {
|
||
super.viewWillDisappear(animated)
|
||
enabledFullScreenPan()
|
||
}
|
||
|
||
private func setupViews() {
|
||
|
||
container.container.scrollView.delegate = self
|
||
}
|
||
|
||
private func setupDatas(){
|
||
// if let editAIInfo = viewModel.editAIInfo, let url = editAIInfo.imageUrl{
|
||
// var usingImage = AIUserImageQuery()
|
||
// usingImage.imageUrl = url
|
||
// usingImage.status = .completed
|
||
// container.appearanceResult.usingFigureImage = usingImage
|
||
// }
|
||
if let editAIInfo = viewModel.editAIInfo{
|
||
viewModel.requestParams.aiUserExt?.imageDesc = editAIInfo.aiUserExt?.imageDesc
|
||
viewModel.requestParams.aiUserExt?.imageStyleCode = editAIInfo.aiUserExt?.imageStyleCode
|
||
}
|
||
|
||
// -- 文案
|
||
var title = "Create"
|
||
var bottomButtonTitle = "Create"
|
||
if viewModel.editAIInfo != nil {
|
||
title = "Modify"
|
||
bottomButtonTitle = "Modify"
|
||
}
|
||
navigationView.alpha0Title = title
|
||
container.titleView.title = title
|
||
container.bottomButton.setTitle(bottomButtonTitle, for: .normal)
|
||
}
|
||
|
||
private func setupEvents(){
|
||
container.backButton.addTarget(self, action: #selector(tapBackButton), for: .touchUpInside)
|
||
container.bottomButton.addTarget(self, action: #selector(bottomButtonTapped), for: .touchUpInside)
|
||
container.generate1Button.addTarget(self, action: #selector(tapAIGeneratedIntroductionButton), for: .touchUpInside)
|
||
container.appearanceResult.generateButton.addTarget(self, action: #selector(tapRegenerateFigureButton), for: .touchUpInside)
|
||
container.uploadImageView.bgTapBlock = {[weak self] in
|
||
//self?.pickAppearance()
|
||
self?.goCreateAIGeneratePics()
|
||
}
|
||
|
||
viewModel.$batchNo.sink {[weak self] result in
|
||
self?.container.showQueryGeneratedImages(generating: !result.isEmpty)
|
||
self?.loadingGeneratingResults(batchNo: result)
|
||
}.store(in: &cancellables)
|
||
|
||
container.$introductionContent.sink {[weak self] string in
|
||
self?.viewModel.requestParams.introduction = string
|
||
}.store(in: &cancellables)
|
||
|
||
if viewModel.editAIInfo == nil{
|
||
doGenerate()
|
||
}else{
|
||
restoreEditInfo(aiInfo: viewModel.editAIInfo)
|
||
}
|
||
}
|
||
|
||
private func restoreEditInfo(aiInfo: AIUserModel?) {
|
||
guard let info = aiInfo else { return }
|
||
|
||
container.privacyPublic = (info.permission ?? 2) == 1
|
||
container.introductionContent = info.introduction ?? ""
|
||
|
||
container.showQueryGeneratedImages(generating: true)
|
||
|
||
// 图片
|
||
let last = AIUserImageQuery()
|
||
last.imageUrl = info.imageUrl
|
||
last.status = .completed
|
||
container.appearanceResult.usingFigureImage = last
|
||
container.appearanceResult.selectImageUrl = info.imageUrl ?? ""
|
||
|
||
//头像
|
||
let photo = UploadPhotoM()
|
||
photo.setupValidImageUrl(url: info.headImg)
|
||
container.avatarModel = photo
|
||
}
|
||
|
||
// MARK: - Functions
|
||
|
||
func loadingGeneratingResults(batchNo : String?){
|
||
if isAllImagesOK {
|
||
return
|
||
}
|
||
|
||
guard let queryNo = batchNo, queryNo.isEmpty == false else{
|
||
dlog("❌batchNo is nil")
|
||
return
|
||
}
|
||
|
||
container.appearanceResult.setupLastGenerateProcessStart()
|
||
|
||
dlog("📖查询图片生成中...")
|
||
AICowProvider.request(.imageGeneratedQuery(batchNo: queryNo), modelType: [AIUserImageQuery].self) { [weak self] result in
|
||
switch result {
|
||
case .success(let success):
|
||
guard let queryDatas = success else{
|
||
return
|
||
}
|
||
self?.container.appearanceResult.config(success)
|
||
var ok = true
|
||
if(queryDatas.count < 6){
|
||
ok = false
|
||
}else{
|
||
for per in queryDatas {
|
||
if per.status == .pending{
|
||
ok = false
|
||
}
|
||
}
|
||
}
|
||
self?.isAllImagesOK = ok
|
||
if ok == false{
|
||
DispatchQueue.main.asyncAfter(deadline: .now() + 6) {[weak self] in
|
||
self?.loadingGeneratingResults(batchNo: self?.viewModel.batchNo)
|
||
}
|
||
}else{
|
||
// ✅
|
||
self?.container.appearanceResult.setupLastGenerateProcessEnd()
|
||
}
|
||
case .failure:
|
||
break
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/// 创建时免费生成图像
|
||
private func goCreateAIGeneratePics(){
|
||
|
||
let vc = RoleFigureGenerateController()
|
||
vc.viewModel = viewModel
|
||
navigationController?.pushViewController(vc, animated: true)
|
||
|
||
vc.generatedBatchNoAction = {[weak self] batchNo, imageStyleCode,description, referenceBase64 in
|
||
self?.viewModel.requestParams.aiUserExt?.imageStyleCode = imageStyleCode
|
||
self?.viewModel.requestParams.aiUserExt?.imageDesc = description
|
||
|
||
// 新图片
|
||
if batchNo.count > 0{
|
||
self?.isAllImagesOK = false
|
||
self?.container.appearanceResult.config([], reset: true) // 先loading着
|
||
self?.container.showQueryGeneratedImages(generating: true)
|
||
self?.loadingGeneratingResults(batchNo: batchNo)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 编辑额外生成图片
|
||
private func goEditAIRegeneratePics(){
|
||
let vc = RolePhotoGenerateController(type: .figure)
|
||
vc.aiId = viewModel.requestParams.aiId
|
||
vc.introduction = container.introductionContent
|
||
vc.sex = viewModel.requestParams.sex
|
||
vc.birthdayString = viewModel.requestParams.birthday
|
||
vc.preSelectstyleCode = viewModel.requestParams.aiUserExt?.imageStyleCode // "IS0020"//
|
||
vc.prePhotoDesc = viewModel.requestParams.aiUserExt?.imageDesc
|
||
presentNaviRootVc(vc: vc)
|
||
|
||
vc.confirmSelectAction = {[weak self] imageStyleCode, desc, imgs in
|
||
self?.viewModel.requestParams.aiUserExt?.imageStyleCode = imageStyleCode
|
||
self?.viewModel.requestParams.aiUserExt?.imageDesc = desc
|
||
|
||
guard let image = imgs?.first else {return}
|
||
self?.container.loadDataAndSelect(image)
|
||
}
|
||
}
|
||
|
||
private func judgeAlertToPublishNewAIRole(){
|
||
// Alert.showAIRoleCreateSuccessAlert {
|
||
// Hud.hideIndicator()
|
||
// } confirmAction: {[weak self] in
|
||
// self?.doPublishRole()
|
||
// }
|
||
|
||
if viewModel.editAIInfo != nil{
|
||
// 编辑AI
|
||
doPublishRole()
|
||
}else{
|
||
// 创建AI Role
|
||
if UserCore.shared.user?.canCreateAIRole() ?? false{
|
||
// 能创建
|
||
Alert.showAIRoleCreateSuccessAlert {
|
||
Hud.hideIndicator()
|
||
} confirmAction: {[weak self] in
|
||
self?.doPublishRole()
|
||
}
|
||
} else {
|
||
if UserCore.shared.user?.isMember == false{ // 去订VIP
|
||
Hud.hideIndicator()
|
||
let sheet = VIPSubscribeSheet()
|
||
sheet.show()
|
||
}else {
|
||
// #warning("是否主动Alert 到底创建限制")
|
||
Alert.showAIRoleCreateSuccessAlert {
|
||
Hud.hideIndicator()
|
||
} confirmAction: {[weak self] in
|
||
self?.doPublishRole()
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
private func doPublishRole(){
|
||
//#warning("test")
|
||
// guard let aiId = viewModel.requestParams.aiId else{
|
||
// return
|
||
// }
|
||
// AppRouter.goBackRootController(jumpIndex: .me)
|
||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||
// AppRouter.goChatVC(aiId: aiId)
|
||
// AppRouter.alertCreateAlbumsToRoleHome(aiId: aiId)
|
||
// }
|
||
// view.hideToastActivity()
|
||
// return
|
||
|
||
// close(dismissFirst: true)
|
||
// AppRouter.goBackRootController(jumpIndex: .me)
|
||
// close(dismissFirst: true) {
|
||
// let vc = UIWindow.getTopViewController()
|
||
// if let current = UIWindow.getTopViewController(), current.isKind(of: RoleHomePagerController.self){
|
||
// // Stay in RoleHomePagerController
|
||
// }else{
|
||
// AppRouter.goBackRootController(jumpIndex: .me)
|
||
// }
|
||
// dlog("🔥current vc: \(String(describing: vc))")
|
||
// }
|
||
// return
|
||
|
||
guard let avatar = container.avatarModel?.remoteFullPath else {
|
||
dlog("invalid ai role avatar")
|
||
return
|
||
}
|
||
|
||
viewModel.requestParams.headImg = avatar
|
||
|
||
viewModel.requestParams.introduction = container.introductionContent
|
||
viewModel.requestParams.permission = container.privacyPublic ? 1 : 2
|
||
var params = viewModel.requestParams.toNonNilDictionary()
|
||
|
||
let editAIInfo = viewModel.editAIInfo
|
||
|
||
params.updateValue(container.appearanceResult.selectImageUrl, forKey: "imageUrl")
|
||
|
||
//dlog("🔥当前params: \(params)")
|
||
//Hud.showIndicator()
|
||
AIRoleProvider.request(.aiUserCreateEdit(params: params), modelType: AICreateResponse.self) {[weak self] result in
|
||
Hud.hideIndicator()
|
||
switch result {
|
||
case let .success(success):
|
||
NotificationCenter.post(name: .aiRoleCreatedOrDelete)
|
||
NotificationCenter.post(name: .aiRoleInfoChanged)
|
||
if editAIInfo != nil{
|
||
dlog("编辑AI 退出")
|
||
// 编辑保存成功
|
||
self?.close(dismissFirst: true) {
|
||
// _ = UIWindow.getTopViewController()
|
||
if let current = UIWindow.getTopViewController(), current.isKind(of: RoleHomePagerController.self){
|
||
// Stay in RoleHomePagerController
|
||
}else{
|
||
AppRouter.goBackRootController(jumpIndex: .me)
|
||
}
|
||
}
|
||
}else{
|
||
// 新建用户
|
||
dlog("新建AI 退出")
|
||
AppRouter.goBackRootController(jumpIndex: .me)
|
||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||
AppRouter.goChatVC(aiId: success?.aiId)
|
||
AppRouter.alertCreateAlbumsToRoleHome(aiId: success?.aiId)
|
||
}
|
||
}
|
||
|
||
case .failure:
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc private func tapAIGeneratedIntroductionButton(){
|
||
if container.introductionContent.count > 0{
|
||
let alert = Alert(title: "内容覆盖", text: "AI创建的内容会覆盖你已经填写的内容,请确认是否继续?")
|
||
let action1 = AlertAction(title: "继续", actionStyle: .confirm) {[weak self] in
|
||
self?.doGenerate()
|
||
}
|
||
let action2 = AlertAction(title: "Cancel", actionStyle: .cancel)
|
||
alert.addAction(action1)
|
||
alert.addAction(action2)
|
||
alert.show()
|
||
}else{
|
||
doGenerate()
|
||
}
|
||
}
|
||
|
||
private func doGenerate(){
|
||
var params = viewModel.requestParams.toPartialDictionary(keys: ["nickname", "sex", "birthday", "characterCode", "tagCode", "introduction"])
|
||
/// 形象描述
|
||
params.updateValue("GEN_INTRODUCTION", forKey: "ptType")
|
||
|
||
if container.introductionContent.count > 0{
|
||
params.updateValue(container.introductionContent, forKey: "content")
|
||
}
|
||
|
||
if let dialogue = viewModel.requestParams.aiUserExt?.dialogueStyle{
|
||
params.updateValue(dialogue, forKey: "dialogue")
|
||
}
|
||
|
||
if let figure = viewModel.requestParams.aiUserExt?.profile{
|
||
params.updateValue(figure, forKey: "figure")
|
||
}
|
||
|
||
Hud.showIndicator()
|
||
AICowProvider.request(.aiContentGenerate(params: params), modelType: AIUserContentGenResponse.self) {[weak self] result in
|
||
Hud.hideIndicator()
|
||
switch result {
|
||
case .success(let success):
|
||
if let content = success?.content{
|
||
self?.container.contentAIGenerated = content
|
||
}
|
||
case .failure:
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc private func tapRegenerateFigureButton(){
|
||
// 尝试重新生成图片
|
||
if viewModel.requestParams.aiId != nil{
|
||
goEditAIRegeneratePics()
|
||
}else{
|
||
goCreateAIGeneratePics()
|
||
}
|
||
}
|
||
|
||
@objc private func bottomButtonTapped(){
|
||
// 头像是否已上传?
|
||
if container.avatarModel?.remoteFullPath != nil{
|
||
Hud.showIndicator()
|
||
judgeAlertToPublishNewAIRole()
|
||
}else{
|
||
// 头像上传...
|
||
guard let photo = container.avatarModel else { return }
|
||
Hud.showIndicator()
|
||
CloudStorage.shared.s3BatchAddPhotos([photo], bucket: .ROLE) {[weak self] result in
|
||
if result{
|
||
//self?.doPublishRole()
|
||
self?.judgeAlertToPublishNewAIRole()
|
||
}else{
|
||
Hud.hideIndicator()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc private func tapBackButton() {
|
||
close()
|
||
}
|
||
}
|
||
|
||
|
||
extension RolePublishController: UIScrollViewDelegate{
|
||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||
NaviAlphaHandle.changeNaviTitleAlpha(scrollView: scrollView, titleLabel: navigationView.titleLabel)
|
||
}
|
||
|
||
}
|