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