Browse Source

上传修改

feature/composer
TianYun 4 weeks ago
parent
commit
e14af359eb
  1. 10
      src/components/AssetLibraryDrawer.tsx
  2. 24
      src/components/AssetLibraryUploadModal.tsx
  3. 9
      src/components/nodes/SmartImageNode.tsx
  4. 9
      src/components/nodes/SmartVideoNode.tsx
  5. 4
      src/i18n/index.tsx

10
src/components/AssetLibraryDrawer.tsx

@ -283,6 +283,7 @@ export function AssetLibraryDrawer({ open, onClose }: AssetLibraryDrawerProps) {
const [newFolderName, setNewFolderName] = useState("");
const [submittingFolder, setSubmittingFolder] = useState(false);
const [uploadModalOpen, setUploadModalOpen] = useState(false);
const [uploadingAsset, setUploadingAsset] = useState(false);
const [actionItemKey, setActionItemKey] = useState<string | null>(null);
const [renamingItemKey, setRenamingItemKey] = useState<string | null>(null);
const [renameValue, setRenameValue] = useState("");
@ -571,10 +572,11 @@ export function AssetLibraryDrawer({ open, onClose }: AssetLibraryDrawerProps) {
}, [loadRootFolders, newFolderName, refreshFolderChildren, t]);
const handleOpenUploadModal = useCallback(() => {
if (uploadingAsset) return;
setAddPopoverOpen(false);
setError(null);
setUploadModalOpen(true);
}, []);
}, [uploadingAsset]);
const handleUploadedAsset = useCallback(async (folderId: number) => {
await refreshFolderChildren(folderId);
@ -730,10 +732,11 @@ export function AssetLibraryDrawer({ open, onClose }: AssetLibraryDrawerProps) {
<div className="nodrag nopan nowheel flex min-w-[176px] flex-col rounded-lg bg-[var(--surface-2)] p-1 text-sm text-[var(--text-secondary)] shadow-[var(--shadow-panel)]">
<button
type="button"
className="flex h-9 items-center gap-2 rounded-md px-3 text-left transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)]"
disabled={uploadingAsset}
className="flex h-9 items-center gap-2 rounded-md px-3 text-left transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)] disabled:cursor-wait disabled:opacity-60 disabled:hover:bg-transparent disabled:hover:text-inherit"
onClick={handleOpenUploadModal}
>
<UploadOutlined className="text-[14px]" />
{uploadingAsset ? <Spin size="small" /> : <UploadOutlined className="text-[14px]" />}
<span>{t("assetLibrary.upload")}</span>
</button>
<button
@ -1192,6 +1195,7 @@ export function AssetLibraryDrawer({ open, onClose }: AssetLibraryDrawerProps) {
onClose={() => setUploadModalOpen(false)}
onUploaded={handleUploadedAsset}
onError={setError}
onUploadingChange={setUploadingAsset}
/>
<AssetLibraryFolderPickerModal
open={movingItem !== null}

24
src/components/AssetLibraryUploadModal.tsx

@ -5,7 +5,7 @@ import { message } from "antd";
import { useI18n } from "@/i18n";
import { authFetch } from "@/utils/authFetch";
import { uploadFileToGatewayMedia } from "@/utils/gatewayMediaUpload";
import { uploadFileToGatewayMedia, type GatewayMediaKind } from "@/utils/gatewayMediaUpload";
import { type UserFileItem, type UserFileMutationResponse } from "@/utils/userFileLibrary";
import { AssetLibraryFolderPickerModal } from "./AssetLibraryFolderPickerModal";
@ -19,6 +19,14 @@ interface AssetLibraryUploadModalProps {
onClose: () => void;
onUploaded: (folderId: number) => void | Promise<void>;
onError?: (message: string) => void;
onUploadingChange?: (uploading: boolean) => void;
}
function getUploadMediaKind(file: File): GatewayMediaKind | null {
if (file.type.startsWith("image/")) return "image";
if (file.type.startsWith("video/")) return "video";
if (file.type.startsWith("audio/")) return "audio";
return null;
}
export function AssetLibraryUploadModal({
@ -31,6 +39,7 @@ export function AssetLibraryUploadModal({
onClose,
onUploaded,
onError,
onUploadingChange,
}: AssetLibraryUploadModalProps) {
const { t } = useI18n();
const [selectedFolderId, setSelectedFolderId] = useState<number | null>(null);
@ -45,6 +54,10 @@ export function AssetLibraryUploadModal({
setSelectedFolderId((current) => current ?? folders[0]?.id ?? null);
}, [folders, open]);
useEffect(() => {
onUploadingChange?.(uploading);
}, [onUploadingChange, uploading]);
const handleCancel = useCallback(() => {
if (uploading) return;
onClose();
@ -61,14 +74,15 @@ export function AssetLibraryUploadModal({
event.target.value = "";
const targetFolderId = uploadTargetFolderIdRef.current ?? selectedId;
if (!file || targetFolderId === null) return;
if (!file.type.startsWith("image/")) {
void message.error(t("assetLibrary.selectImageFile"));
const mediaKind = getUploadMediaKind(file);
if (!mediaKind) {
void message.error(t("assetLibrary.selectMediaFile"));
return;
}
setUploading(true);
try {
const uploaded = await uploadFileToGatewayMedia(file, "image");
const uploaded = await uploadFileToGatewayMedia(file, mediaKind);
const response = await authFetch("/api/popi/user-file/upload", {
method: "POST",
headers: { "Content-Type": "application/json" },
@ -100,7 +114,7 @@ export function AssetLibraryUploadModal({
<input
ref={inputRef}
type="file"
accept="image/*"
accept="image/*,video/*,audio/*"
className="hidden"
onChange={handleFileChange}
/>

9
src/components/nodes/SmartImageNode.tsx

@ -1,7 +1,7 @@
"use client";
import { useCallback, useRef, useState } from "react";
import { Node, NodeProps } from "@xyflow/react";
import { useCallback, useEffect, useRef, useState } from "react";
import { Node, NodeProps, useUpdateNodeInternals } from "@xyflow/react";
import { Spin } from "antd";
import { AssetPickerModal, PopiAssetItem } from "@/components/AssetPickerModal";
import { useI18n } from "@/i18n";
@ -308,8 +308,13 @@ function SmartImageNeutralView({
export function SmartImageNode({ id, data, selected }: NodeProps<SmartImageNodeType>) {
const edges = useWorkflowStore((state) => state.edges);
const providerEnvStatus = useWorkflowStore((state) => state.providerEnvStatus);
const updateNodeInternals = useUpdateNodeInternals();
const mode = deriveSmartImageMode(id, data, edges);
useEffect(() => {
updateNodeInternals(id);
}, [id, mode, updateNodeInternals]);
if (mode === "generate") {
if (isSmartGenerateUploadEnabled(providerEnvStatus)) {
return <SmartImageNeutralView id={id} data={data} selected={selected} addToGenerationHistory />;

9
src/components/nodes/SmartVideoNode.tsx

@ -1,7 +1,7 @@
"use client";
import { useCallback, useRef, useState } from "react";
import { Node, NodeProps } from "@xyflow/react";
import { useCallback, useEffect, useRef, useState } from "react";
import { Node, NodeProps, useUpdateNodeInternals } from "@xyflow/react";
import { Spin } from "antd";
import { AssetPickerModal, PopiAssetItem } from "@/components/AssetPickerModal";
import { useI18n } from "@/i18n";
@ -334,8 +334,13 @@ function SmartVideoNeutralView({
export function SmartVideoNode({ id, data, selected }: NodeProps<SmartVideoNodeType>) {
const edges = useWorkflowStore((state) => state.edges);
const providerEnvStatus = useWorkflowStore((state) => state.providerEnvStatus);
const updateNodeInternals = useUpdateNodeInternals();
const mode = deriveSmartVideoMode(id, data, edges);
useEffect(() => {
updateNodeInternals(id);
}, [id, mode, updateNodeInternals]);
if (mode === "generate") {
if (isSmartGenerateUploadEnabled(providerEnvStatus)) {
return <SmartVideoNeutralView id={id} data={data} selected={selected} addToGenerationHistory />;

4
src/i18n/index.tsx

@ -74,6 +74,7 @@ const en = {
"assetLibrary.selectFolder": "Select folder",
"assetLibrary.noFolders": "No folders available",
"assetLibrary.selectImageFile": "Please select an image file",
"assetLibrary.selectMediaFile": "Please select an image, video, or audio file",
"assetLibrary.uploadFailed": "Upload failed",
"assetLibrary.uploadSuccess": "Uploaded",
"assetLibrary.itemActions": "Item actions",
@ -1207,6 +1208,7 @@ const zhCN: Dictionary = {
"assetLibrary.selectFolder": "选择文件夹",
"assetLibrary.noFolders": "暂无文件夹",
"assetLibrary.selectImageFile": "请选择图片文件",
"assetLibrary.selectMediaFile": "请选择图片、视频或音频文件",
"assetLibrary.uploadFailed": "上传失败",
"assetLibrary.uploadSuccess": "上传成功",
"assetLibrary.itemActions": "素材操作",
@ -2108,6 +2110,7 @@ const zhTW: Dictionary = {
"assetLibrary.selectFolder": "選擇資料夾",
"assetLibrary.noFolders": "暫無資料夾",
"assetLibrary.selectImageFile": "請選擇圖片檔案",
"assetLibrary.selectMediaFile": "請選擇圖片、影片或音訊檔案",
"assetLibrary.uploadFailed": "上傳失敗",
"assetLibrary.uploadSuccess": "上傳成功",
"assetLibrary.itemActions": "素材操作",
@ -2796,6 +2799,7 @@ const ja: Dictionary = {
"assetLibrary.selectFolder": "フォルダを選択",
"assetLibrary.noFolders": "フォルダがありません",
"assetLibrary.selectImageFile": "画像ファイルを選択してください",
"assetLibrary.selectMediaFile": "画像、動画、または音声ファイルを選択してください",
"assetLibrary.uploadFailed": "アップロードに失敗しました",
"assetLibrary.uploadSuccess": "アップロードしました",
"assetLibrary.itemActions": "アイテム操作",

Loading…
Cancel
Save