diff --git a/src/views/FlexibleGenerationView.vue b/src/views/FlexibleGenerationView.vue index 68ab9b5..ca56c67 100644 --- a/src/views/FlexibleGenerationView.vue +++ b/src/views/FlexibleGenerationView.vue @@ -13,6 +13,7 @@ import MultiSelect from 'primevue/multiselect' import ProgressSpinner from 'primevue/progressspinner' import ProgressBar from 'primevue/progressbar' import Message from 'primevue/message' +import Skeleton from 'primevue/skeleton' const router = useRouter() const API_URL = import.meta.env.VITE_API_URL @@ -21,6 +22,12 @@ const API_URL = import.meta.env.VITE_API_URL const prompt = ref('') const selectedCharacter = ref(null) const selectedAssets = ref([]) +// Asset Picker State +const isAssetPickerVisible = ref(false) +const assetPickerTab = ref('all') // 'all', 'uploaded', 'generated' +const modalAssets = ref([]) +const isModalLoading = ref(false) +const tempSelectedAssets = ref([]) const quality = ref({ key: 'TWOK', value: '2K' }) const aspectRatio = ref({ key: "NINESIXTEEN", value: "9:16" }) const sendToTelegram = ref(false) @@ -34,7 +41,7 @@ const characters = ref([]) const allAssets = ref([]) const historyGenerations = ref([]) const historyTotal = ref(0) -const historyRows = ref(20) +const historyRows = ref(50) const historyFirst = ref(0) const isSettingsVisible = ref(false) @@ -43,7 +50,7 @@ const generationStatus = ref('') const generationProgress = ref(0) const generationError = ref(null) const generatedResult = ref(null) // For immediate feedback if needed -const isLoadingMore = ref(false) +const activeOverlayId = ref(null) // For mobile tap-to-show overlay // Options const qualityOptions = ref([ @@ -159,31 +166,35 @@ const loadData = async () => { } } -const loadMoreHistory = async () => { - if (isLoadingMore.value || historyGenerations.value.length >= historyTotal.value) return - - isLoadingMore.value = true - historyFirst.value += historyRows.value - +const refreshHistory = async () => { try { - const response = await aiService.getGenerations(historyRows.value, historyFirst.value) + const response = await aiService.getGenerations(historyRows.value, 0) if (response && response.generations) { - historyGenerations.value = [...historyGenerations.value, ...response.generations] + // Update existing items and add new ones at the top + const newGenerations = [] + for (const gen of response.generations) { + const existingIndex = historyGenerations.value.findIndex(g => g.id === gen.id) + if (existingIndex !== -1) { + // Update existing item in place to preserve state/reactivity + Object.assign(historyGenerations.value[existingIndex], gen) + } else { + newGenerations.push(gen) + } + } + + // Add completely new items to the start of the list + if (newGenerations.length > 0) { + historyGenerations.value = [...newGenerations, ...historyGenerations.value] + } + historyTotal.value = response.total_count || historyTotal.value } } catch (e) { - console.error('Failed to load more history', e) - } finally { - isLoadingMore.value = false + console.error('Failed to refresh history', e) } } -const onScroll = (event) => { - const { scrollTop, clientHeight, scrollHeight } = event.target - if (scrollTop + clientHeight >= scrollHeight - 50) { - loadMoreHistory() - } -} + // --- Generation --- const handleGenerate = async () => { @@ -240,12 +251,8 @@ const pollStatus = async (id) => { if (response.status === 'done') { completed = true - // Refresh history to show new item - const historyRes = await aiService.getGenerations(historyRows.value, 0) - if (historyRes && historyRes.generations) { - historyGenerations.value = historyRes.generations - historyTotal.value = historyRes.total_count || 0 - } + // Refresh history to show new item without resetting list + await refreshHistory() } else if (response.status === 'failed') { completed = true generationError.value = response.failed_reason || 'Generation failed' @@ -265,9 +272,6 @@ const pollStatus = async (id) => { // --- Initial Load --- onMounted(() => { loadData() - // Open settings by default if it's a new user or explicitly requested? - // Maybe better to keep it closed or open based on layout preference. - // Let's keep it open initially for better UX as they likely want to generate. isSettingsVisible.value = true }) @@ -371,15 +375,73 @@ const deleteGeneration = async (gen) => { } } +const toggleMobileOverlay = (id) => { + // Only for touch/small screens if needed, or just general tap behavior + if (activeOverlayId.value === id) { + activeOverlayId.value = null + } else { + activeOverlayId.value = id + } +} + +// --- Asset Picker Logic --- + +const loadModalAssets = async () => { + isModalLoading.value = true + try { + const typeParam = assetPickerTab.value === 'all' ? undefined : assetPickerTab.value + // Use a larger limit for the modal or implement scrolling/pagination if needed. + // For now, 100 should be enough for a demo, or we can add pagination later. + const response = await dataService.getAssets(100, 0, typeParam) + + if (response && response.assets) { + modalAssets.value = response.assets + } else { + modalAssets.value = Array.isArray(response) ? response : [] + } + } catch (e) { + console.error('Failed to load modal assets', e) + modalAssets.value = [] + } finally { + isModalLoading.value = false + } +} + +const openAssetPicker = () => { + tempSelectedAssets.value = [...selectedAssets.value] + isAssetPickerVisible.value = true + loadModalAssets() +} + +const toggleAssetSelection = (asset) => { + const index = tempSelectedAssets.value.findIndex(a => a.id === asset.id) + if (index === -1) { + tempSelectedAssets.value.push(asset) + } else { + tempSelectedAssets.value.splice(index, 1) + } +} + +const confirmAssetSelection = () => { + selectedAssets.value = [...tempSelectedAssets.value] + isAssetPickerVisible.value = false +} + +const removeAsset = (asset) => { + selectedAssets.value = selectedAssets.value.filter(a => a.id !== asset.id) +} + +watch(assetPickerTab, () => { + if (isAssetPickerVisible.value) { + loadModalAssets() + } +}) +