Files
waooplus/src/lib/query/mutations/character-image-ops-mutations.ts

244 lines
7.9 KiB
TypeScript

import { useMutation, useQueryClient } from '@tanstack/react-query'
import { queryKeys } from '../keys'
import { apiFetch } from '@/lib/api-fetch'
import {
clearTaskTargetOverlay,
upsertTaskTargetOverlay,
} from '../task-target-overlay'
import {
invalidateQueryTemplates,
requestJsonWithError,
requestTaskResponseWithError,
} from './mutation-shared'
import { resolveTaskResponse } from '@/lib/task/client'
export function useModifyProjectCharacterImage(projectId: string) {
const queryClient = useQueryClient()
const invalidateProjectAssetAndProjectData = () =>
invalidateQueryTemplates(queryClient, [
queryKeys.projectAssets.all(projectId),
queryKeys.projectData(projectId),
])
return useMutation({
mutationFn: async (params: {
characterId: string
appearanceId: string
imageIndex: number
modifyPrompt: string
extraImageUrls?: string[]
}) => {
const response = await requestTaskResponseWithError(`/api/assets/${params.characterId}/modify-render`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
scope: 'project',
kind: 'character',
projectId,
...params,
}),
}, 'Failed to modify image')
return await resolveTaskResponse(response)
},
onMutate: ({ appearanceId }) => {
upsertTaskTargetOverlay(queryClient, {
projectId,
targetType: 'CharacterAppearance',
targetId: appearanceId,
intent: 'modify',
})
},
onError: (_error, { appearanceId }) => {
clearTaskTargetOverlay(queryClient, {
projectId,
targetType: 'CharacterAppearance',
targetId: appearanceId,
})
},
onSettled: invalidateProjectAssetAndProjectData,
})
}
/**
* 修改项目场景图片
*/
export function useRegenerateCharacterGroup(projectId: string) {
const queryClient = useQueryClient()
const invalidateProjectAssets = () =>
invalidateQueryTemplates(queryClient, [queryKeys.projectAssets.all(projectId)])
return useMutation({
mutationFn: async ({
characterId,
appearanceId,
count,
}: {
characterId: string
appearanceId: string
count?: number
}) => {
return await requestJsonWithError(`/api/assets/${characterId}/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
scope: 'project',
kind: 'character',
projectId,
appearanceId,
count,
})
}, 'Failed to regenerate group')
},
onMutate: ({ appearanceId }) => {
upsertTaskTargetOverlay(queryClient, {
projectId,
targetType: 'CharacterAppearance',
targetId: appearanceId,
intent: 'regenerate',
})
},
onError: (_error, { appearanceId }) => {
clearTaskTargetOverlay(queryClient, {
projectId,
targetType: 'CharacterAppearance',
targetId: appearanceId,
})
},
onSettled: invalidateProjectAssets,
})
}
/**
* 重新生成单张角色图片
*/
export function useRegenerateSingleCharacterImage(projectId: string) {
const queryClient = useQueryClient()
const invalidateProjectAssets = () =>
invalidateQueryTemplates(queryClient, [queryKeys.projectAssets.all(projectId)])
return useMutation({
mutationFn: async ({
characterId,
appearanceId,
imageIndex,
}: {
characterId: string
appearanceId: string
imageIndex: number
}) => {
return await requestJsonWithError(`/api/assets/${characterId}/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
scope: 'project',
kind: 'character',
projectId,
appearanceId,
imageIndex,
})
}, 'Failed to regenerate image')
},
onMutate: ({ appearanceId }) => {
upsertTaskTargetOverlay(queryClient, {
projectId,
targetType: 'CharacterAppearance',
targetId: appearanceId,
intent: 'regenerate',
})
},
onError: (_error, { appearanceId }) => {
clearTaskTargetOverlay(queryClient, {
projectId,
targetType: 'CharacterAppearance',
targetId: appearanceId,
})
},
onSettled: invalidateProjectAssets,
})
}
/**
* 重新生成场景组图片
*/
export function useUpdateProjectAppearanceDescription(projectId: string) {
const queryClient = useQueryClient()
const invalidateProjectAssets = () =>
invalidateQueryTemplates(queryClient, [queryKeys.projectAssets.all(projectId)])
return useMutation({
mutationFn: async ({
characterId,
appearanceId,
description,
descriptionIndex,
}: {
characterId: string
appearanceId: string
description: string
descriptionIndex?: number
}) => {
return await requestJsonWithError(`/api/assets/${characterId}/variants/${appearanceId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
scope: 'project',
kind: 'character',
projectId,
description,
descriptionIndex: typeof descriptionIndex === 'number' ? descriptionIndex : 0,
}),
}, 'Failed to update appearance description')
},
onSuccess: invalidateProjectAssets,
})
}
export function useBatchGenerateCharacterImages(projectId: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (items: Array<{ characterId: string; appearanceId: string }>) => {
const results = await Promise.allSettled(
items.map(item =>
apiFetch(`/api/assets/${item.characterId}/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
scope: 'project',
kind: 'character',
projectId,
appearanceId: item.appearanceId
})
})
)
)
return results
},
onMutate: (items) => {
for (const item of items) {
upsertTaskTargetOverlay(queryClient, {
projectId,
targetType: 'CharacterAppearance',
targetId: item.appearanceId,
intent: 'generate',
})
}
},
onError: (_error, items) => {
for (const item of items) {
clearTaskTargetOverlay(queryClient, {
projectId,
targetType: 'CharacterAppearance',
targetId: item.appearanceId,
})
}
},
onSettled: () => {
invalidateQueryTemplates(queryClient, [queryKeys.projectAssets.all(projectId)])
}
})
}