mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-21 01:12:40 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc892076d0 |
@@ -0,0 +1,40 @@
|
||||
import { ApiManager, ApiResponse } from "./ApiManager";
|
||||
|
||||
export interface NewVersionInfo {
|
||||
version_label: string;
|
||||
features_description: string;
|
||||
direct_download_link?: string;
|
||||
website_download_link: string;
|
||||
}
|
||||
|
||||
export interface VersionInfoResponse {
|
||||
is_up_to_date: boolean;
|
||||
new_version?: NewVersionInfo;
|
||||
}
|
||||
|
||||
export class VersionApi extends ApiManager {
|
||||
private static instance: VersionApi;
|
||||
|
||||
public static getInstance(): VersionApi {
|
||||
if (!VersionApi.instance) {
|
||||
VersionApi.instance = new VersionApi();
|
||||
}
|
||||
return VersionApi.instance;
|
||||
}
|
||||
|
||||
public getVersionInfo(
|
||||
platform: string,
|
||||
versionString: string,
|
||||
): Promise<ApiResponse<VersionInfoResponse>> {
|
||||
const endpoint = `${this.getApiSchemeAndHost()}/v1/artcraft/version_info/${platform}/${versionString}`;
|
||||
return this.get<VersionInfoResponse>({ endpoint })
|
||||
.then((data) => ({
|
||||
success: true,
|
||||
data,
|
||||
}))
|
||||
.catch((err) => ({
|
||||
success: false,
|
||||
errorMessage: err.message,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -17,3 +17,4 @@ export * from "./UserBookmarksApi";
|
||||
export * from "./UsersApi";
|
||||
export * from "./VideoApi";
|
||||
export * from "./WeightsApi";
|
||||
export * from "./VersionApi";
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
import {
|
||||
faArrowAltUp,
|
||||
faTriangleExclamation,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { gtagEvent } from "@storyteller/google-analytics";
|
||||
import { GetAppInfo, OpenUrl } from "@storyteller/tauri-api";
|
||||
import { useTauriPlatform } from "@storyteller/tauri-utils";
|
||||
import { Button } from "@storyteller/ui-button";
|
||||
import { Tooltip } from "@storyteller/ui-tooltip";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { MiscApi, VersionApi, NewVersionInfo } from "~/Classes/ApiManager";
|
||||
|
||||
export const AppStatusCheck = () => {
|
||||
const [updateInfo, setUpdateInfo] = useState<NewVersionInfo | null>(null);
|
||||
const platform = useTauriPlatform();
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
const checkAppStatus = async () => {
|
||||
try {
|
||||
const appInfo = await GetAppInfo();
|
||||
const version = appInfo.payload.artcraft_version;
|
||||
const currentPlatform =
|
||||
platform || appInfo.payload.os_platform || "windows";
|
||||
|
||||
let versionRes = await VersionApi.getInstance().getVersionInfo(
|
||||
currentPlatform,
|
||||
version,
|
||||
);
|
||||
|
||||
// TODO: Remove this mock once the API is working!
|
||||
if (!versionRes.success || !versionRes.data) {
|
||||
console.log(
|
||||
"API not working - injecting mock version info for testing",
|
||||
);
|
||||
versionRes = {
|
||||
success: true,
|
||||
data: {
|
||||
is_up_to_date: false,
|
||||
new_version: {
|
||||
version_label: "v1.5.0-beta",
|
||||
features_description:
|
||||
"New AI generation tools, UI polish, and bug fixes!",
|
||||
direct_download_link: "https://getartcraft.com/download/direct",
|
||||
website_download_link: "https://getartcraft.com/download",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
active &&
|
||||
versionRes.success &&
|
||||
versionRes.data &&
|
||||
!versionRes.data.is_up_to_date &&
|
||||
versionRes.data.new_version
|
||||
) {
|
||||
const newVersion = versionRes.data.new_version;
|
||||
setUpdateInfo(newVersion);
|
||||
|
||||
setTimeout(() => {
|
||||
if (active) {
|
||||
toast(
|
||||
(t) => (
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
toast.dismiss(t.id);
|
||||
if (newVersion.direct_download_link) {
|
||||
OpenUrl(newVersion.direct_download_link);
|
||||
} else {
|
||||
OpenUrl(newVersion.website_download_link);
|
||||
}
|
||||
gtagEvent("click_update_button");
|
||||
}}
|
||||
>
|
||||
Update available: {newVersion.version_label}
|
||||
</div>
|
||||
),
|
||||
{
|
||||
duration: 10000,
|
||||
icon: (
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowAltUp}
|
||||
className="text-blue-600"
|
||||
/>
|
||||
),
|
||||
style: {
|
||||
background: "#eff6ff",
|
||||
color: "#1e3a8a",
|
||||
border: "1px solid #bfdbfe",
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
let outageRes = await new MiscApi().GetStatusAlertCheck();
|
||||
|
||||
// TODO: Remove this mock once the API is working!
|
||||
if (!outageRes.success || !outageRes.data?.maybe_alert?.maybe_message) {
|
||||
console.log(
|
||||
"API not working - injecting mock outage info for testing",
|
||||
);
|
||||
outageRes = {
|
||||
success: true,
|
||||
data: {
|
||||
refresh_interval_millis: 60000,
|
||||
maybe_alert: {
|
||||
maybe_category: "outage",
|
||||
maybe_message:
|
||||
"We are currently experiencing a brief outage. The team is actively investigating.",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
active &&
|
||||
outageRes.success &&
|
||||
outageRes.data?.maybe_alert?.maybe_message
|
||||
) {
|
||||
// Add a small delay for the toast to ensure it doesn't get hidden by initial loads
|
||||
setTimeout(() => {
|
||||
if (active) {
|
||||
toast(outageRes.data!.maybe_alert!.maybe_message!, {
|
||||
duration: 15000,
|
||||
icon: (
|
||||
<FontAwesomeIcon
|
||||
icon={faTriangleExclamation}
|
||||
className="text-red-700"
|
||||
/>
|
||||
),
|
||||
|
||||
style: {
|
||||
background: "#fef2f2",
|
||||
color: "#991b1b",
|
||||
border: "1px solid #f87171",
|
||||
},
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to check app status", err);
|
||||
}
|
||||
};
|
||||
checkAppStatus();
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [platform]);
|
||||
|
||||
if (!updateInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
content={`Update: ${updateInfo.version_label} - ${updateInfo.features_description}`}
|
||||
position="bottom"
|
||||
delay={300}
|
||||
>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="h-[38px] transition-all duration-300 hover:animate-none hover:shadow-none"
|
||||
onClick={() => {
|
||||
if (updateInfo.direct_download_link) {
|
||||
OpenUrl(updateInfo.direct_download_link);
|
||||
} else {
|
||||
OpenUrl(updateInfo.website_download_link);
|
||||
}
|
||||
gtagEvent("click_update_button");
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowAltUp} className="animate-bounce" />
|
||||
Update
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -76,6 +76,7 @@ import { AppsQuickMenu } from "./AppsQuickMenu";
|
||||
import { SceneTitleInput } from "./SceneTitleInput";
|
||||
import { TaskQueue } from "./TaskQueue";
|
||||
import { UploadImagesButton } from "./UploadImagesButton";
|
||||
import { AppStatusCheck } from "./AppStatusCheck";
|
||||
|
||||
interface Props {
|
||||
pageName: string;
|
||||
@@ -166,7 +167,7 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
Promise.all([
|
||||
creditsStore.fetchFromServer(),
|
||||
subscriptionStore.fetchFromServer(),
|
||||
])
|
||||
]);
|
||||
}, 1000);
|
||||
return () => clearTimeout(t);
|
||||
}, []);
|
||||
@@ -597,6 +598,8 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<AppStatusCheck />
|
||||
|
||||
<div className="flex justify-end gap-2" data-tauri-drag-region>
|
||||
<div className="no-drag flex items-center gap-1.5">
|
||||
{isCreditsLoading ? (
|
||||
@@ -607,7 +610,10 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
position="bottom"
|
||||
align="center"
|
||||
triggerIcon={
|
||||
<FontAwesomeIcon icon={faCoins} className="text-primary" />
|
||||
<FontAwesomeIcon
|
||||
icon={faCoins}
|
||||
className="text-primary"
|
||||
/>
|
||||
}
|
||||
triggerLabel={
|
||||
<span className="whitespace-nowrap text-sm font-medium">
|
||||
|
||||
@@ -8,6 +8,7 @@ export type TabId =
|
||||
| "EDIT"
|
||||
| "IMAGE"
|
||||
| "APPS"
|
||||
| "EDIT"
|
||||
| "VIDEO_FRAME_EXTRACTOR"
|
||||
| "VIDEO_WATERMARK_REMOVAL"
|
||||
| "IMAGE_WATERMARK_REMOVAL"
|
||||
|
||||
Reference in New Issue
Block a user