mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-06 01:17:48 +00:00
feat(locations): remember last Add-window directory
Remember manually selected Add-window folders during the current session, with a persisted Locations toggle and cross-platform path handling.\n\nRefs #23
This commit is contained in:
@@ -15,9 +15,11 @@ import { DuplicateResolutionModal, DuplicateConflict } from './DuplicateResoluti
|
||||
import { canonicalizeDownloadFileName, categoryForFileName } from '../utils/downloads';
|
||||
import { fetchMediaMetadataDeduped, fetchMediaPlaylistMetadataDeduped } from '../utils/mediaMetadata';
|
||||
import {
|
||||
expandTilde,
|
||||
resolveCategoryDestination,
|
||||
resolveDownloadFilePath,
|
||||
downloadLocationEquals
|
||||
downloadLocationEquals,
|
||||
resolveInitialAddWindowLocation
|
||||
} from '../utils/downloadLocations';
|
||||
import { getPlatformInfo } from '../utils/platform';
|
||||
import { isTransferLocked } from '../utils/downloadActions';
|
||||
@@ -130,6 +132,8 @@ export const AddDownloadsModal = () => {
|
||||
} = useDownloadStore();
|
||||
const {
|
||||
baseDownloadFolder,
|
||||
rememberLastUsedDownloadDirectory,
|
||||
lastUsedDownloadDirectory,
|
||||
perServerConnections,
|
||||
keychainAccessReady,
|
||||
keychainPromptDismissed,
|
||||
@@ -158,6 +162,9 @@ export const AddDownloadsModal = () => {
|
||||
// Right Form
|
||||
const [saveLocation, setSaveLocation] = useState(baseDownloadFolder);
|
||||
const [isSaveLocationManual, setIsSaveLocationManual] = useState(false);
|
||||
const locationResolutionRequestRef = useRef(0);
|
||||
const folderPickerRequestRef = useRef(0);
|
||||
const pendingLastUsedDownloadDirectoryRef = useRef<string | null>(null);
|
||||
const [connections, setConnections] = useState(perServerConnections);
|
||||
const [speedLimitEnabled, setSpeedLimitEnabled] = useState(false);
|
||||
const [speedLimit, setSpeedLimit] = useState('1024');
|
||||
@@ -222,6 +229,8 @@ export const AddDownloadsModal = () => {
|
||||
useEffect(() => {
|
||||
if (!isAddModalOpen) {
|
||||
modalSessionRef.current = false;
|
||||
++folderPickerRequestRef.current;
|
||||
pendingLastUsedDownloadDirectoryRef.current = null;
|
||||
setUrls('');
|
||||
setPlaylistExpansions({});
|
||||
playlistRequestsRef.current.clear();
|
||||
@@ -238,8 +247,13 @@ export const AddDownloadsModal = () => {
|
||||
? requestContextForUrl(initialUrlLines[0])
|
||||
: undefined;
|
||||
|
||||
setSaveLocation(baseDownloadFolder);
|
||||
setIsSaveLocationManual(false);
|
||||
const initialLocation = resolveInitialAddWindowLocation(
|
||||
baseDownloadFolder,
|
||||
rememberLastUsedDownloadDirectory,
|
||||
lastUsedDownloadDirectory
|
||||
);
|
||||
setSaveLocation(initialLocation.path);
|
||||
setIsSaveLocationManual(initialLocation.isManual);
|
||||
setUrls(initialUrls);
|
||||
setParsedItems([]);
|
||||
setPlaylistExpansions({});
|
||||
@@ -279,6 +293,8 @@ export const AddDownloadsModal = () => {
|
||||
pendingAddCookies,
|
||||
pendingAddMediaUrls,
|
||||
baseDownloadFolder,
|
||||
rememberLastUsedDownloadDirectory,
|
||||
lastUsedDownloadDirectory,
|
||||
perServerConnections
|
||||
]);
|
||||
|
||||
@@ -609,6 +625,13 @@ export const AddDownloadsModal = () => {
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!rememberLastUsedDownloadDirectory) {
|
||||
pendingLastUsedDownloadDirectoryRef.current = null;
|
||||
}
|
||||
}, [rememberLastUsedDownloadDirectory]);
|
||||
|
||||
useEffect(() => {
|
||||
const requestId = ++locationResolutionRequestRef.current;
|
||||
if (parsedItems.length === 0) {
|
||||
setSelectedItemIndex(null);
|
||||
return;
|
||||
@@ -618,7 +641,12 @@ export const AddDownloadsModal = () => {
|
||||
);
|
||||
if (isSaveLocationManual) return;
|
||||
if (parsedItems.length > 1) {
|
||||
setSaveLocation(useSettingsStore.getState().baseDownloadFolder || '~/Downloads');
|
||||
const baseFolder = useSettingsStore.getState().baseDownloadFolder || '~/Downloads';
|
||||
void expandTilde(baseFolder).then(location => {
|
||||
if (requestId === locationResolutionRequestRef.current) {
|
||||
setSaveLocation(location);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
const first = parsedItems[0];
|
||||
@@ -626,22 +654,35 @@ export const AddDownloadsModal = () => {
|
||||
void resolveCategoryDestination(
|
||||
useSettingsStore.getState(),
|
||||
categoryForFileName(first.file)
|
||||
).then(setSaveLocation);
|
||||
).then(location => {
|
||||
if (requestId === locationResolutionRequestRef.current) {
|
||||
setSaveLocation(location);
|
||||
}
|
||||
});
|
||||
}, [isSaveLocationManual, parsedItems]);
|
||||
|
||||
if (!isAddModalOpen) return null;
|
||||
|
||||
const handleBrowse = async () => {
|
||||
try {
|
||||
const requestId = ++folderPickerRequestRef.current;
|
||||
const defaultPath = await expandTilde(saveLocation);
|
||||
const selected = await open({
|
||||
directory: true,
|
||||
multiple: false,
|
||||
defaultPath: saveLocation.startsWith('~') ? undefined : saveLocation
|
||||
defaultPath
|
||||
});
|
||||
if (requestId !== folderPickerRequestRef.current) return;
|
||||
if (selected && typeof selected === 'string') {
|
||||
++locationResolutionRequestRef.current;
|
||||
const approvedPath = await useSettingsStore.getState().approveDownloadRoot(selected);
|
||||
if (requestId !== folderPickerRequestRef.current) return;
|
||||
setSaveLocation(approvedPath);
|
||||
setIsSaveLocationManual(true);
|
||||
const settings = useSettingsStore.getState();
|
||||
if (settings.rememberLastUsedDownloadDirectory) {
|
||||
pendingLastUsedDownloadDirectoryRef.current = approvedPath;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to select folder:", e);
|
||||
@@ -663,6 +704,7 @@ export const AddDownloadsModal = () => {
|
||||
}
|
||||
isSubmittingRef.current = true;
|
||||
setIsSubmitting(true);
|
||||
++folderPickerRequestRef.current;
|
||||
let finalLocation = saveLocation;
|
||||
let useSharedDestination = isSaveLocationManual;
|
||||
const destinationOverrides: Record<number, string> = {};
|
||||
@@ -679,18 +721,24 @@ export const AddDownloadsModal = () => {
|
||||
directory: true,
|
||||
multiple: false,
|
||||
title: `Choose a folder for ${item.file}`,
|
||||
defaultPath: suggestedLocation.startsWith('~') ? undefined : suggestedLocation
|
||||
defaultPath: await expandTilde(suggestedLocation)
|
||||
});
|
||||
if (selected && typeof selected === 'string') {
|
||||
const approvedPath = await useSettingsStore.getState().approveDownloadRoot(selected);
|
||||
destinationOverrides[index] = approvedPath;
|
||||
const currentSettings = useSettingsStore.getState();
|
||||
if (currentSettings.rememberLastUsedDownloadDirectory) {
|
||||
pendingLastUsedDownloadDirectoryRef.current = approvedPath;
|
||||
}
|
||||
} else {
|
||||
pendingLastUsedDownloadDirectoryRef.current = null;
|
||||
isSubmittingRef.current = false;
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to select folder:", e);
|
||||
pendingLastUsedDownloadDirectoryRef.current = null;
|
||||
isSubmittingRef.current = false;
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
@@ -981,6 +1029,17 @@ export const AddDownloadsModal = () => {
|
||||
failures.push(`${item.file}: ${e instanceof Error ? e.message : String(e)}`);
|
||||
}
|
||||
}
|
||||
const currentSettings = useSettingsStore.getState();
|
||||
if (
|
||||
addedCount > 0
|
||||
&& currentSettings.rememberLastUsedDownloadDirectory
|
||||
&& pendingLastUsedDownloadDirectoryRef.current
|
||||
) {
|
||||
currentSettings.setLastUsedDownloadDirectory(
|
||||
pendingLastUsedDownloadDirectoryRef.current
|
||||
);
|
||||
}
|
||||
pendingLastUsedDownloadDirectoryRef.current = null;
|
||||
toggleAddModal(false);
|
||||
if (failures.length > 0) {
|
||||
addToast({
|
||||
@@ -1357,7 +1416,8 @@ export const AddDownloadsModal = () => {
|
||||
/>
|
||||
<button
|
||||
onClick={handleBrowse}
|
||||
className="add-download-button add-download-button-secondary px-3 text-xs font-medium"
|
||||
disabled={isSubmitting}
|
||||
className="add-download-button add-download-button-secondary px-3 text-xs font-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{t($ => $.addDownloads.browse)}
|
||||
</button>
|
||||
|
||||
@@ -1069,6 +1069,21 @@ runEngineChecks(false);
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="mac-settings-group">
|
||||
<label className="mac-settings-row cursor-default">
|
||||
<div>
|
||||
<span className="text-[13px] text-text-primary">{t($ => $.settings.locations.rememberLastUsedDownloadDirectory)}</span>
|
||||
<p className="mt-0.5 text-[11px] text-text-muted">{t($ => $.settings.locations.rememberLastUsedDownloadDirectoryDescription)}</p>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.rememberLastUsedDownloadDirectory}
|
||||
onChange={(e) => settings.setRememberLastUsedDownloadDirectory(e.target.checked)}
|
||||
className="mac-switch"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="mac-settings-group">
|
||||
<label className="mac-settings-row cursor-default">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user