mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-09 19:41:32 +00:00
app: ask location before download (#1483)
- toggle in settings to enable disable ask location on download button pressed
This commit is contained in:
1 parent
5a7c253189
commit
f666e68dcd
8 files changed
+133
-26
No files matched your search
@@ -17,7 +17,11 @@ import {
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { FilterMediaClasses } from "@storyteller/api";
|
||||
import {
|
||||
FilterMediaClasses,
|
||||
downloadUrlToPath,
|
||||
promptDownloadLocationIfNeeded,
|
||||
} from "@storyteller/api";
|
||||
import {
|
||||
getCreatorIcon,
|
||||
ModelCreator,
|
||||
@@ -288,8 +292,16 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
|
||||
const downloadFile = async (url: string, mediaClass?: string) => {
|
||||
try {
|
||||
//await downloadFileFromUrl(url);
|
||||
await DownloadUrl(url);
|
||||
const chosenPath = await promptDownloadLocationIfNeeded(url);
|
||||
if (chosenPath === null) {
|
||||
// User dismissed the picker.
|
||||
return;
|
||||
}
|
||||
if (typeof chosenPath === "string") {
|
||||
await downloadUrlToPath(url, chosenPath);
|
||||
} else {
|
||||
await DownloadUrl(url);
|
||||
}
|
||||
if (mediaClass === FilterMediaClasses.DIMENSIONAL) {
|
||||
toast.success(`Downloaded 3D model`);
|
||||
} else {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.promise.d.ts","../../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/@remix-run/node/dist/globals.d.ts","../../node_modules/@types/cookie/index.d.ts","../../node_modules/@remix-run/server-runtime/dist/crypto.d.ts","../../node_modules/@remix-run/server-runtime/dist/cookies.d.ts","../../node_modules/@remix-run/server-runtime/dLine truncated
|
||||
{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.promise.d.ts","../../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/@remix-run/node/dist/globals.d.ts","../../node_modules/@types/cookie/index.d.ts","../../node_modules/@remix-run/server-runtime/dist/crypto.d.ts","../../node_modules/@remix-run/server-runtime/dist/cookies.d.ts","../../node_modules/@remix-run/server-runtime/dLine truncated
|
||||
@@ -16,6 +16,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/plugin-dialog": "^2.2.1",
|
||||
"@tauri-apps/plugin-upload": "2.2.1"
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,91 @@
|
||||
|
||||
import { download } from "@tauri-apps/plugin-upload";
|
||||
import { downloadDir } from "@tauri-apps/api/path";
|
||||
import { save } from "@tauri-apps/plugin-dialog";
|
||||
|
||||
const ASK_LOCATION_BEFORE_DOWNLOAD_KEY = "artcraft_ask_location_before_download";
|
||||
|
||||
export const getAskLocationBeforeDownload = (): boolean => {
|
||||
if (typeof window === "undefined") return false;
|
||||
try {
|
||||
return window.localStorage.getItem(ASK_LOCATION_BEFORE_DOWNLOAD_KEY) === "true";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const setAskLocationBeforeDownload = (enabled: boolean): void => {
|
||||
if (typeof window === "undefined") return;
|
||||
try {
|
||||
if (enabled) {
|
||||
window.localStorage.setItem(ASK_LOCATION_BEFORE_DOWNLOAD_KEY, "true");
|
||||
} else {
|
||||
window.localStorage.removeItem(ASK_LOCATION_BEFORE_DOWNLOAD_KEY);
|
||||
}
|
||||
} catch {
|
||||
// ignore storage failures
|
||||
}
|
||||
};
|
||||
|
||||
const deriveDownloadFilename = (url: string): string => {
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const last = urlObj.pathname.split("/").pop();
|
||||
if (last && last.length > 0) return last;
|
||||
} catch {
|
||||
// fall through
|
||||
}
|
||||
return "downloaded_file";
|
||||
};
|
||||
|
||||
/**
|
||||
* Prompts the user with a native save dialog if the
|
||||
* "Ask location before download" setting is on.
|
||||
*
|
||||
* Returns:
|
||||
* - the chosen absolute path when the user picked one
|
||||
* - `null` when the user dismissed the dialog (caller should abort)
|
||||
* - `undefined` when the toggle is off (caller should fall back to default)
|
||||
*/
|
||||
export const promptDownloadLocationIfNeeded = async (
|
||||
url: string,
|
||||
): Promise<string | null | undefined> => {
|
||||
if (!getAskLocationBeforeDownload()) return undefined;
|
||||
const filename = deriveDownloadFilename(url);
|
||||
const chosen = await save({ defaultPath: filename });
|
||||
return chosen ?? null;
|
||||
};
|
||||
|
||||
/** Downloads `url` to an explicit absolute filesystem path. */
|
||||
export const downloadUrlToPath = async (url: string, path: string) => {
|
||||
await download(url, path);
|
||||
};
|
||||
|
||||
export const downloadFileFromUrl = async (url: string) => {
|
||||
console.log("GOT THE URL", url);
|
||||
try {
|
||||
// Extract filename from URL
|
||||
const urlObj = new URL(url);
|
||||
let filename = urlObj.pathname.split("/").pop();
|
||||
if (!filename || filename === "") {
|
||||
filename = "downloaded_file";
|
||||
}
|
||||
console.log("GOT THE URL", url);
|
||||
try {
|
||||
const filename = deriveDownloadFilename(url);
|
||||
|
||||
// Get downloads directory path
|
||||
const downloadsPath = await downloadDir();
|
||||
const filePath = `${downloadsPath}/${filename}`;
|
||||
|
||||
// Download and save the file
|
||||
await download(url, filePath);
|
||||
|
||||
console.log(
|
||||
`File downloaded and saved as ${filename} in downloads folder`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error downloading file:", error);
|
||||
throw error;
|
||||
let filePath: string;
|
||||
const chosen = await promptDownloadLocationIfNeeded(url);
|
||||
if (chosen === null) {
|
||||
// User dismissed the picker.
|
||||
return;
|
||||
}
|
||||
};
|
||||
if (typeof chosen === "string") {
|
||||
filePath = chosen;
|
||||
} else {
|
||||
const downloadsPath = await downloadDir();
|
||||
filePath = `${downloadsPath}/${filename}`;
|
||||
}
|
||||
|
||||
await download(url, filePath);
|
||||
|
||||
console.log(
|
||||
`File downloaded and saved to ${filePath}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error downloading file:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
{"fileNames":[],"fileInfos":[],"root":[],"options":{"composite":true,"declarationMap":true,"emitDeclarationOnly":true,"importHelpers":true,"module":99,"noEmitOnError":true,"noFallthroughCasesInSwitch":true,"noImplicitOverride":true,"noImplicitReturns":true,"noUnusedLocals":true,"skipLibCheck":true,"strict":true,"target":9},"version":"5.8.3"}
|
||||
@@ -18,6 +18,10 @@ import {
|
||||
faRotateLeft,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { useEnterToGenerateStore } from "@storyteller/ui-promptbox";
|
||||
import {
|
||||
getAskLocationBeforeDownload,
|
||||
setAskLocationBeforeDownload,
|
||||
} from "@storyteller/api";
|
||||
|
||||
interface MiscSettingsPaneProps {}
|
||||
|
||||
@@ -29,6 +33,14 @@ export const MiscSettingsPane = (args: MiscSettingsPaneProps) => {
|
||||
const enterToGenerate = useEnterToGenerateStore((s) => s.enabled);
|
||||
const setEnterToGenerate = useEnterToGenerateStore((s) => s.setEnabled);
|
||||
|
||||
const [askLocationBeforeDownload, setAskLocationBeforeDownloadState] =
|
||||
useState<boolean>(() => getAskLocationBeforeDownload());
|
||||
|
||||
const toggleAskLocationBeforeDownload = (enabled: boolean) => {
|
||||
setAskLocationBeforeDownload(enabled);
|
||||
setAskLocationBeforeDownloadState(enabled);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const prefs = await GetAppPreferences();
|
||||
@@ -113,6 +125,23 @@ export const MiscSettingsPane = (args: MiscSettingsPaneProps) => {
|
||||
Show Directory
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 pt-3">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<Label htmlFor="ask-location-before-download">
|
||||
Ask location before download
|
||||
</Label>
|
||||
<p className="text-xs opacity-70">
|
||||
When on, a system file picker appears every time you download from
|
||||
the lightbox or anywhere in the app, letting you choose the save
|
||||
location for that file. When off, downloads go straight to the
|
||||
default download directory above.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
enabled={askLocationBeforeDownload}
|
||||
setEnabled={toggleAskLocationBeforeDownload}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 pt-3">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<Label htmlFor="enter-to-generate">Enter to generate</Label>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"fileNames":[],"fileInfos":[],"root":[],"options":{"composite":true,"declarationMap":true,"emitDeclarationOnly":true,"importHelpers":true,"module":99,"noEmitOnError":true,"noFallthroughCasesInSwitch":true,"noImplicitOverride":true,"noImplicitReturns":true,"noUnusedLocals":true,"skipLibCheck":true,"strict":true,"target":9},"version":"5.8.3"}
|
||||
Generated
+1
@@ -476,6 +476,7 @@
|
||||
"name": "@storyteller/api",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@tauri-apps/plugin-dialog": "^2.2.1",
|
||||
"@tauri-apps/plugin-upload": "2.2.1"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user