|
|
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
|
|
Monitor,
|
|
|
|
|
Smartphone,
|
|
|
|
|
ArrowLeft,
|
|
|
|
|
RefreshCw,
|
|
|
|
|
} from "lucide-react";
|
|
|
|
|
import { createStyles, useTheme } from "antd-style";
|
|
|
|
|
import {
|
|
|
|
|
@@ -101,9 +102,12 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
const fileChangeStore = useFileChangeStore();
|
|
|
|
|
const { message } = AntApp.useApp();
|
|
|
|
|
|
|
|
|
|
const [downloadingKey, setDownloadingKey] = React.useState<string | null>(null);
|
|
|
|
|
const [downloadingKey, setDownloadingKey] = React.useState<string | null>(
|
|
|
|
|
null,
|
|
|
|
|
);
|
|
|
|
|
const [downloadingAll, setDownloadingAll] = React.useState(false);
|
|
|
|
|
const [deploying, setDeploying] = React.useState(false);
|
|
|
|
|
const [refreshing, setRefreshing] = React.useState(false);
|
|
|
|
|
|
|
|
|
|
// Reset view mode when app changes
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
@@ -131,7 +135,9 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
fileName: getDisplayName(file, "js", index),
|
|
|
|
|
fileType: "js" as const,
|
|
|
|
|
platform: "desktop" as const,
|
|
|
|
|
size: isFileResource(file) ? parseInt(file.file.size ?? "0", 10) || undefined : undefined,
|
|
|
|
|
size: isFileResource(file)
|
|
|
|
|
? parseInt(file.file.size ?? "0", 10) || undefined
|
|
|
|
|
: undefined,
|
|
|
|
|
fileKey: getFileKey(file),
|
|
|
|
|
url: isUrlResource(file) ? file.url : undefined,
|
|
|
|
|
})),
|
|
|
|
|
@@ -140,7 +146,9 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
fileName: getDisplayName(file, "css", index),
|
|
|
|
|
fileType: "css" as const,
|
|
|
|
|
platform: "desktop" as const,
|
|
|
|
|
size: isFileResource(file) ? parseInt(file.file.size ?? "0", 10) || undefined : undefined,
|
|
|
|
|
size: isFileResource(file)
|
|
|
|
|
? parseInt(file.file.size ?? "0", 10) || undefined
|
|
|
|
|
: undefined,
|
|
|
|
|
fileKey: getFileKey(file),
|
|
|
|
|
url: isUrlResource(file) ? file.url : undefined,
|
|
|
|
|
})),
|
|
|
|
|
@@ -149,7 +157,9 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
fileName: getDisplayName(file, "js", index),
|
|
|
|
|
fileType: "js" as const,
|
|
|
|
|
platform: "mobile" as const,
|
|
|
|
|
size: isFileResource(file) ? parseInt(file.file.size ?? "0", 10) || undefined : undefined,
|
|
|
|
|
size: isFileResource(file)
|
|
|
|
|
? parseInt(file.file.size ?? "0", 10) || undefined
|
|
|
|
|
: undefined,
|
|
|
|
|
fileKey: getFileKey(file),
|
|
|
|
|
url: isUrlResource(file) ? file.url : undefined,
|
|
|
|
|
})),
|
|
|
|
|
@@ -158,7 +168,9 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
fileName: getDisplayName(file, "css", index),
|
|
|
|
|
fileType: "css" as const,
|
|
|
|
|
platform: "mobile" as const,
|
|
|
|
|
size: isFileResource(file) ? parseInt(file.file.size ?? "0", 10) || undefined : undefined,
|
|
|
|
|
size: isFileResource(file)
|
|
|
|
|
? parseInt(file.file.size ?? "0", 10) || undefined
|
|
|
|
|
: undefined,
|
|
|
|
|
fileKey: getFileKey(file),
|
|
|
|
|
url: isUrlResource(file) ? file.url : undefined,
|
|
|
|
|
})),
|
|
|
|
|
@@ -168,7 +180,7 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
}, [currentApp?.appId]);
|
|
|
|
|
|
|
|
|
|
const loadAppDetail = async () => {
|
|
|
|
|
if (!currentDomain || !selectedAppId) return;
|
|
|
|
|
if (!currentDomain || !selectedAppId) return undefined;
|
|
|
|
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
@@ -180,23 +192,23 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
// Check if we're still on the same app and component is mounted before updating
|
|
|
|
|
if (result.success) {
|
|
|
|
|
setCurrentApp(result.data);
|
|
|
|
|
return result.data;
|
|
|
|
|
}
|
|
|
|
|
return undefined;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to load app detail:", error);
|
|
|
|
|
return undefined;
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleFileClick = useCallback(
|
|
|
|
|
(fileKey: string, name: string) => {
|
|
|
|
|
const handleFileClick = useCallback((fileKey: string, name: string) => {
|
|
|
|
|
const ext = name.split(".").pop()?.toLowerCase();
|
|
|
|
|
const type = ext === "css" ? "css" : "js";
|
|
|
|
|
setSelectedFile({ type, fileKey, name });
|
|
|
|
|
setViewMode("code");
|
|
|
|
|
},
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const handleBackToList = useCallback(() => {
|
|
|
|
|
setViewMode("list");
|
|
|
|
|
@@ -244,7 +256,9 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
if (saveResult.success) {
|
|
|
|
|
message.success(t("downloadSuccess", { ns: "common" }));
|
|
|
|
|
} else {
|
|
|
|
|
message.error(saveResult.error || t("downloadFailed", { ns: "common" }));
|
|
|
|
|
message.error(
|
|
|
|
|
saveResult.error || t("downloadFailed", { ns: "common" }),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
message.error(t("downloadFailed", { ns: "common" }));
|
|
|
|
|
@@ -256,7 +270,8 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const handleDownloadAll = useCallback(async () => {
|
|
|
|
|
if (!currentDomain || !selectedAppId || downloadingAll || !currentApp) return;
|
|
|
|
|
if (!currentDomain || !selectedAppId || downloadingAll || !currentApp)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const appName = currentApp.name || "app";
|
|
|
|
|
const sanitizedAppName = appName.replace(/[:*?"<>|]/g, "_");
|
|
|
|
|
@@ -280,7 +295,9 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
message.success(t("downloadAllSuccess", { path: result.data?.path, ns: "common" }));
|
|
|
|
|
message.success(
|
|
|
|
|
t("downloadAllSuccess", { path: result.data?.path, ns: "common" }),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
message.error(result.error || t("downloadFailed", { ns: "common" }));
|
|
|
|
|
}
|
|
|
|
|
@@ -323,9 +340,7 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
message.error(result.error || t("deployFailed"));
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
message.error(
|
|
|
|
|
error instanceof Error ? error.message : t("deployFailed"),
|
|
|
|
|
);
|
|
|
|
|
message.error(error instanceof Error ? error.message : t("deployFailed"));
|
|
|
|
|
} finally {
|
|
|
|
|
setDeploying(false);
|
|
|
|
|
}
|
|
|
|
|
@@ -359,7 +374,8 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const changeCount = currentDomain && selectedAppId
|
|
|
|
|
const changeCount =
|
|
|
|
|
currentDomain && selectedAppId
|
|
|
|
|
? fileChangeStore.getChangeCount(currentDomain.id, selectedAppId)
|
|
|
|
|
: { added: 0, deleted: 0 };
|
|
|
|
|
const hasChanges = changeCount.added > 0 || changeCount.deleted > 0;
|
|
|
|
|
@@ -373,6 +389,13 @@ const AppDetail: React.FC = () => {
|
|
|
|
|
<Tag>ID: {currentApp.appId}</Tag>
|
|
|
|
|
</div>
|
|
|
|
|
<Space>
|
|
|
|
|
<Button
|
|
|
|
|
icon={<RefreshCw size={16} />}
|
|
|
|
|
loading={refreshing}
|
|
|
|
|
onClick={handleRefresh}
|
|
|
|
|
>
|
|
|
|
|
{t("refresh", { ns: "common" })}
|
|
|
|
|
</Button>
|
|
|
|
|
<Button icon={<History size={16} />}>
|
|
|
|
|
{t("versionHistory", { ns: "common" })}
|
|
|
|
|
</Button>
|
|
|
|
|
|