likes
This commit is contained in:
@@ -29,12 +29,12 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['toggle-select', 'open-preview', 'toggle-like', 'delete', 'reuse-prompt', 'reuse-asset', 'use-result', 'toggle-overlay'])
|
const emit = defineEmits(['toggle-select', 'open-preview', 'toggle-like', 'delete', 'reuse-prompt', 'reuse-asset', 'use-result', 'toggle-overlay', 'mark-nsfw'])
|
||||||
|
|
||||||
const isTemporarilyUnblurred = ref(false)
|
const isTemporarilyUnblurred = ref(false)
|
||||||
|
|
||||||
const isBlurred = computed(() => {
|
const isBlurred = computed(() => {
|
||||||
return props.generation.nsfw && !props.showNsfwGlobal && !isTemporarilyUnblurred.value
|
return (props.generation.is_nsfw || props.generation.nsfw) && !props.showNsfwGlobal && !isTemporarilyUnblurred.value
|
||||||
})
|
})
|
||||||
|
|
||||||
const toggleBlur = () => {
|
const toggleBlur = () => {
|
||||||
@@ -46,14 +46,6 @@ const handleImageClick = (e) => {
|
|||||||
emit('toggle-select', props.generation.result_list[0])
|
emit('toggle-select', props.generation.result_list[0])
|
||||||
} else {
|
} else {
|
||||||
if (isBlurred.value) {
|
if (isBlurred.value) {
|
||||||
// If blurred, click might just unblur or do nothing?
|
|
||||||
// Let's let the button handle unblur, and click opens preview if unblurred?
|
|
||||||
// Or maybe click unblurs? Let's stick to button for unblur to be explicit.
|
|
||||||
// But if user clicks image, maybe show preview anyway?
|
|
||||||
// Usually blurred images shouldn't be previewed full size unless unblurred.
|
|
||||||
// Let's allow preview, but maybe preview also needs to handle blur?
|
|
||||||
// For now, let's just open preview. The preview modal might need its own blur logic or just show it.
|
|
||||||
// Let's assume preview shows it.
|
|
||||||
emit('open-preview', props.apiUrl + '/assets/' + props.generation.result_list[0])
|
emit('open-preview', props.apiUrl + '/assets/' + props.generation.result_list[0])
|
||||||
} else {
|
} else {
|
||||||
emit('open-preview', props.apiUrl + '/assets/' + props.generation.result_list[0])
|
emit('open-preview', props.apiUrl + '/assets/' + props.generation.result_list[0])
|
||||||
@@ -138,6 +130,10 @@ const handleOverlayClick = () => {
|
|||||||
<Button icon="pi pi-pencil"
|
<Button icon="pi pi-pencil"
|
||||||
class="!w-6 !h-6 !rounded-full !bg-white/20 !border-none !text-white text-[10px] hover:!bg-violet-500"
|
class="!w-6 !h-6 !rounded-full !bg-white/20 !border-none !text-white text-[10px] hover:!bg-violet-500"
|
||||||
@click.stop="emit('use-result', generation)" />
|
@click.stop="emit('use-result', generation)" />
|
||||||
|
<Button :icon="(generation.is_nsfw || generation.nsfw) ? 'pi pi-eye' : 'pi pi-eye-slash'"
|
||||||
|
class="!w-6 !h-6 !rounded-full !bg-white/20 !border-none !text-white text-[10px] hover:!bg-red-500 hover:!text-white"
|
||||||
|
@click.stop="emit('mark-nsfw', generation)"
|
||||||
|
v-tooltip.bottom="(generation.is_nsfw || generation.nsfw) ? 'Unmark NSFW' : 'Mark NSFW'" />
|
||||||
<Button icon="pi pi-trash"
|
<Button icon="pi pi-trash"
|
||||||
class="!w-6 !h-6 !rounded-full !bg-red-500/20 !border-none !text-red-400 text-[10px] hover:!bg-red-500 hover:!text-white"
|
class="!w-6 !h-6 !rounded-full !bg-red-500/20 !border-none !text-red-400 text-[10px] hover:!bg-red-500 hover:!text-white"
|
||||||
@click.stop="emit('delete', generation)" />
|
@click.stop="emit('delete', generation)" />
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ export const aiService = {
|
|||||||
return response.data
|
return response.data
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Mark generation as NSFW
|
||||||
|
async markGenerationNsfw(generationId, isNsfw = true) {
|
||||||
|
const response = await api.post(`/generations/${generationId}/nsfw`, {
|
||||||
|
is_nsfw: isNsfw
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
},
|
||||||
|
|
||||||
// Get usage statistics (runs, tokens, cost)
|
// Get usage statistics (runs, tokens, cost)
|
||||||
async getUsageReport(breakdown = null, projectId = null) {
|
async getUsageReport(breakdown = null, projectId = null) {
|
||||||
const params = {}
|
const params = {}
|
||||||
|
|||||||
@@ -844,6 +844,29 @@ const useResultAsReference = (gen) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const markNsfw = async (gen) => {
|
||||||
|
// Determine new state (toggle)
|
||||||
|
const currentNsfw = gen.is_nsfw || gen.nsfw || false
|
||||||
|
const newNsfw = !currentNsfw
|
||||||
|
|
||||||
|
try {
|
||||||
|
await aiService.markGenerationNsfw(gen.id, newNsfw)
|
||||||
|
|
||||||
|
// Update local state
|
||||||
|
gen.is_nsfw = newNsfw
|
||||||
|
// Also update legacy property if present to keep UI consistent
|
||||||
|
if (gen.nsfw !== undefined) gen.nsfw = newNsfw
|
||||||
|
|
||||||
|
// If this is the currently displayed result, update it too
|
||||||
|
if (generatedResult.value && generatedResult.value.assets && generatedResult.value.assets.some(a => gen.result_list.includes(a.id))) {
|
||||||
|
generatedResult.value.is_nsfw = newNsfw
|
||||||
|
if (generatedResult.value.nsfw !== undefined) generatedResult.value.nsfw = newNsfw
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to toggle NSFW', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const triggerFileUpload = () => {
|
const triggerFileUpload = () => {
|
||||||
if (fileInput.value) fileInput.value.click()
|
if (fileInput.value) fileInput.value.click()
|
||||||
}
|
}
|
||||||
@@ -1378,6 +1401,7 @@ const handleGenerate = async () => {
|
|||||||
<span class="capitalize"
|
<span class="capitalize"
|
||||||
:class="gen.status === 'done' ? 'text-green-500' : (gen.status === 'failed' ? 'text-red-500' : 'text-amber-500')">{{
|
:class="gen.status === 'done' ? 'text-green-500' : (gen.status === 'failed' ? 'text-red-500' : 'text-amber-500')">{{
|
||||||
gen.status }}</span>
|
gen.status }}</span>
|
||||||
|
<Tag v-if="gen.is_nsfw || gen.nsfw" value="NSFW" severity="danger" class="!text-[8px] !py-0 !px-1" />
|
||||||
<i v-if="gen.failed_reason"
|
<i v-if="gen.failed_reason"
|
||||||
v-tooltip.right="gen.failed_reason"
|
v-tooltip.right="gen.failed_reason"
|
||||||
class="pi pi-exclamation-circle text-red-500"
|
class="pi pi-exclamation-circle text-red-500"
|
||||||
@@ -1414,6 +1438,11 @@ const handleGenerate = async () => {
|
|||||||
:disabled="gen.status !== 'done' || gen.result_list.length == 0"
|
:disabled="gen.status !== 'done' || gen.result_list.length == 0"
|
||||||
@click.stop="useResultAsReference(gen)"
|
@click.stop="useResultAsReference(gen)"
|
||||||
v-tooltip.bottom="'Use result as reference'" />
|
v-tooltip.bottom="'Use result as reference'" />
|
||||||
|
<Button :icon="(gen.is_nsfw || gen.nsfw) ? 'pi pi-eye' : 'pi pi-eye-slash'"
|
||||||
|
label="NSFW" size="small" text
|
||||||
|
class="!text-[10px] !py-0.5 !px-2 text-slate-400 hover:bg-white/5 flex-1"
|
||||||
|
@click.stop="markNsfw(gen)"
|
||||||
|
v-tooltip.bottom="(gen.is_nsfw || gen.nsfw) ? 'Unmark NSFW' : 'Mark NSFW'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -752,6 +752,27 @@ const toggleMobileOverlay = (id) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const markNsfw = async (gen) => {
|
||||||
|
// if (!confirm('Are you sure you want to mark this generation as NSFW?')) return
|
||||||
|
const currentNsfw = gen.is_nsfw || gen.nsfw || false
|
||||||
|
const newNsfw = !currentNsfw
|
||||||
|
|
||||||
|
try {
|
||||||
|
await aiService.markGenerationNsfw(gen.id, newNsfw)
|
||||||
|
gen.is_nsfw = newNsfw
|
||||||
|
if (gen.nsfw !== undefined) gen.nsfw = newNsfw
|
||||||
|
|
||||||
|
if (gen.isGroup && gen.children) {
|
||||||
|
gen.children.forEach(c => {
|
||||||
|
c.is_nsfw = newNsfw
|
||||||
|
if (c.nsfw !== undefined) c.nsfw = newNsfw
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to toggle NSFW', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Asset Picker Logic ---
|
// --- Asset Picker Logic ---
|
||||||
|
|
||||||
const loadModalAssets = async () => {
|
const loadModalAssets = async () => {
|
||||||
@@ -892,7 +913,7 @@ const confirmAddToAlbum = async () => {
|
|||||||
<div class="flex items-center gap-1.5">
|
<div class="flex items-center gap-1.5">
|
||||||
<img v-if="slotProps.option.avatar_image" :src="API_URL + slotProps.option.avatar_image"
|
<img v-if="slotProps.option.avatar_image" :src="API_URL + slotProps.option.avatar_image"
|
||||||
class="w-5 h-5 rounded-full object-cover" />
|
class="w-5 h-5 rounded-full object-cover" />
|
||||||
<span class="">{{ slotProps.option.name }}</span>
|
<span class="text-sm">{{ slotProps.option.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
@@ -955,6 +976,7 @@ const confirmAddToAlbum = async () => {
|
|||||||
@reuse-asset="reuseAsset"
|
@reuse-asset="reuseAsset"
|
||||||
@use-result="useResultAsAsset"
|
@use-result="useResultAsAsset"
|
||||||
@toggle-overlay="toggleMobileOverlay"
|
@toggle-overlay="toggleMobileOverlay"
|
||||||
|
@mark-nsfw="markNsfw"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -979,6 +1001,7 @@ const confirmAddToAlbum = async () => {
|
|||||||
@reuse-asset="reuseAsset"
|
@reuse-asset="reuseAsset"
|
||||||
@use-result="useResultAsAsset"
|
@use-result="useResultAsAsset"
|
||||||
@toggle-overlay="toggleMobileOverlay"
|
@toggle-overlay="toggleMobileOverlay"
|
||||||
|
@mark-nsfw="markNsfw"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -958,6 +958,26 @@ watch(viewMode, (v) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const markNsfw = async (gen) => {
|
||||||
|
// if (!confirm('Are you sure you want to mark this generation as NSFW?')) return
|
||||||
|
const currentNsfw = gen.is_nsfw || gen.nsfw || false
|
||||||
|
const newNsfw = !currentNsfw
|
||||||
|
|
||||||
|
try {
|
||||||
|
await aiService.markGenerationNsfw(gen.id, newNsfw)
|
||||||
|
gen.is_nsfw = newNsfw
|
||||||
|
if (gen.nsfw !== undefined) gen.nsfw = newNsfw
|
||||||
|
|
||||||
|
if (gen.isGroup && gen.children) {
|
||||||
|
gen.children.forEach(c => {
|
||||||
|
c.is_nsfw = newNsfw
|
||||||
|
if (c.nsfw !== undefined) c.nsfw = newNsfw
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to toggle NSFW', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1077,6 +1097,11 @@ watch(viewMode, (v) => {
|
|||||||
text rounded size="small"
|
text rounded size="small"
|
||||||
class="!w-7 !h-7 !text-slate-400 hover:!text-violet-400"
|
class="!w-7 !h-7 !text-slate-400 hover:!text-violet-400"
|
||||||
v-tooltip.top="'Reuse Assets'" @click="reuseAssets(gen)" />
|
v-tooltip.top="'Reuse Assets'" @click="reuseAssets(gen)" />
|
||||||
|
<Button :icon="(gen.is_nsfw || gen.nsfw) ? 'pi pi-eye' : 'pi pi-eye-slash'"
|
||||||
|
text rounded size="small"
|
||||||
|
class="!w-7 !h-7 !text-slate-400 hover:!text-red-400"
|
||||||
|
v-tooltip.top="(gen.is_nsfw || gen.nsfw) ? 'Unmark NSFW' : 'Mark NSFW'"
|
||||||
|
@click="markNsfw(gen)" />
|
||||||
<Button icon="pi pi-trash" text rounded size="small"
|
<Button icon="pi pi-trash" text rounded size="small"
|
||||||
class="!w-7 !h-7 !text-slate-400 hover:!text-red-400"
|
class="!w-7 !h-7 !text-slate-400 hover:!text-red-400"
|
||||||
v-tooltip.top="'Delete'" @click="deleteGeneration(gen)" />
|
v-tooltip.top="'Delete'" @click="deleteGeneration(gen)" />
|
||||||
@@ -1216,6 +1241,11 @@ watch(viewMode, (v) => {
|
|||||||
@click.stop="setAsReference(img.assetId)" />
|
@click.stop="setAsReference(img.assetId)" />
|
||||||
<Button icon="pi pi-refresh" rounded text size="small"
|
<Button icon="pi pi-refresh" rounded text size="small"
|
||||||
class="!text-white hover:!bg-white/20" @click.stop="reusePrompt(img.gen)" />
|
class="!text-white hover:!bg-white/20" @click.stop="reusePrompt(img.gen)" />
|
||||||
|
<Button :icon="(img.gen.is_nsfw || img.gen.nsfw) ? 'pi pi-eye' : 'pi pi-eye-slash'"
|
||||||
|
rounded text size="small"
|
||||||
|
class="!text-white hover:!bg-red-500/20 hover:!text-red-400"
|
||||||
|
v-tooltip.top="(img.gen.is_nsfw || img.gen.nsfw) ? 'Unmark NSFW' : 'Mark NSFW'"
|
||||||
|
@click.stop="markNsfw(img.gen)" />
|
||||||
<Button icon="pi pi-trash" rounded text size="small"
|
<Button icon="pi pi-trash" rounded text size="small"
|
||||||
class="!text-red-400 hover:!bg-red-500/20"
|
class="!text-red-400 hover:!bg-red-500/20"
|
||||||
@click.stop="deleteAssetFromGeneration(img.gen, img.assetId)" />
|
@click.stop="deleteAssetFromGeneration(img.gen, img.assetId)" />
|
||||||
@@ -1678,4 +1708,4 @@ watch(viewMode, (v) => {
|
|||||||
.fade-leave-to {
|
.fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user