mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-12 12:37:04 +00:00
feat(downloads): add opt-in batch folders (#27)
This commit is contained in:
@@ -95,6 +95,8 @@ describe('useDownloadStore', () => {
|
||||
pendingAddHeaders: '',
|
||||
pendingAddCookies: '',
|
||||
pendingAddMediaUrls: [],
|
||||
pendingAddBatch: false,
|
||||
pendingAddBatchName: '',
|
||||
pendingAddRequestContexts: {},
|
||||
pendingAddRequestVersion: 0,
|
||||
});
|
||||
@@ -1720,7 +1722,9 @@ describe('useDownloadStore', () => {
|
||||
{ url: 'https://mail.google.com/', cookies: 'SID=mail-session' },
|
||||
{ url: 'https://accounts.google.com/', cookies: 'SID=account-session' }
|
||||
],
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
const state = useDownloadStore.getState();
|
||||
@@ -1756,7 +1760,9 @@ describe('useDownloadStore', () => {
|
||||
headers: 'User-Agent: Firefox Test',
|
||||
cookies: null,
|
||||
cookie_scopes: null,
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
const state = useDownloadStore.getState();
|
||||
@@ -1777,7 +1783,9 @@ describe('useDownloadStore', () => {
|
||||
headers: 'User-Agent: Test',
|
||||
cookies: 'session=secret',
|
||||
cookie_scopes: null,
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
const state = useDownloadStore.getState();
|
||||
@@ -1790,6 +1798,30 @@ describe('useDownloadStore', () => {
|
||||
expect(state.pendingAddMediaUrls).toEqual([]);
|
||||
});
|
||||
|
||||
it('tracks selected-link batch context without changing ordinary multi-link handoffs', async () => {
|
||||
await useDownloadStore.getState().handleExtensionDownload({
|
||||
urls: ['https://example.com/one.zip', 'https://example.com/two.zip'],
|
||||
referer: 'https://example.com/gallery',
|
||||
silent: false,
|
||||
filename: null,
|
||||
headers: null,
|
||||
cookies: null,
|
||||
cookie_scopes: null,
|
||||
media: false,
|
||||
batch: true,
|
||||
batch_name: 'Example Gallery'
|
||||
});
|
||||
|
||||
expect(useDownloadStore.getState().pendingAddBatch).toBe(true);
|
||||
expect(useDownloadStore.getState().pendingAddBatchName).toBe('Example Gallery');
|
||||
|
||||
useDownloadStore.getState().toggleAddModal(false);
|
||||
useDownloadStore.getState().openAddModalWithUrls(
|
||||
'https://example.com/one.zip\nhttps://example.com/two.zip'
|
||||
);
|
||||
expect(useDownloadStore.getState().pendingAddBatch).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps each extension handoff context attached to its own URL while the Add Modal is open', async () => {
|
||||
await useDownloadStore.getState().handleExtensionDownload({
|
||||
urls: ['https://first.example/file.zip'],
|
||||
@@ -1799,7 +1831,9 @@ describe('useDownloadStore', () => {
|
||||
headers: 'User-Agent: First Browser',
|
||||
cookies: 'first=session',
|
||||
cookie_scopes: null,
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
await useDownloadStore.getState().handleExtensionDownload({
|
||||
urls: ['https://second.example/file.zip'],
|
||||
@@ -1809,7 +1843,9 @@ describe('useDownloadStore', () => {
|
||||
headers: 'User-Agent: Second Browser',
|
||||
cookies: 'second=session',
|
||||
cookie_scopes: null,
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
const state = useDownloadStore.getState();
|
||||
@@ -1846,7 +1882,9 @@ describe('useDownloadStore', () => {
|
||||
headers: `Cookie: stale=${'x'.repeat(64 * 1024)}\nUser-Agent: Firefox Test`,
|
||||
cookies: `oversized=${'x'.repeat(64 * 1024)}`,
|
||||
cookie_scopes: null,
|
||||
media: true
|
||||
media: true,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
const state = useDownloadStore.getState();
|
||||
@@ -1866,7 +1904,9 @@ describe('useDownloadStore', () => {
|
||||
headers: null,
|
||||
cookies: 'session=secret',
|
||||
cookie_scopes: null,
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
expect(useDownloadStore.getState().pendingAddCookies).toBe('session=secret');
|
||||
@@ -1883,7 +1923,9 @@ describe('useDownloadStore', () => {
|
||||
cookie_scopes: [
|
||||
{ url: 'https://media.example/', cookies: 'session=secret' }
|
||||
],
|
||||
media: true
|
||||
media: true,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
expect(useDownloadStore.getState().pendingAddRequestContexts['https://media.example/watch/123']?.cookieScopes)
|
||||
@@ -1900,7 +1942,9 @@ describe('useDownloadStore', () => {
|
||||
headers: 'Authorization: secret',
|
||||
cookies: 'session=secret',
|
||||
cookie_scopes: null,
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
await useDownloadStore.getState().handleExtensionDownload({
|
||||
urls: [url],
|
||||
@@ -1910,7 +1954,9 @@ describe('useDownloadStore', () => {
|
||||
headers: null,
|
||||
cookies: null,
|
||||
cookie_scopes: null,
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
expect(useDownloadStore.getState().pendingAddRequestContexts[url]).toEqual({
|
||||
@@ -1938,7 +1984,9 @@ describe('useDownloadStore', () => {
|
||||
headers: 'User-Agent: Firefox Test',
|
||||
cookies: 'session=secret',
|
||||
cookie_scopes: null,
|
||||
media: true
|
||||
media: true,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
expect(useDownloadStore.getState().pendingAddMediaUrls).toEqual([
|
||||
@@ -1958,7 +2006,9 @@ describe('useDownloadStore', () => {
|
||||
headers: 'User-Agent: Firefox Test',
|
||||
cookies: null,
|
||||
cookie_scopes: null,
|
||||
media: false
|
||||
media: false,
|
||||
batch: false,
|
||||
batch_name: null
|
||||
});
|
||||
|
||||
expect(useDownloadStore.getState().pendingAddMediaUrls).toEqual([]);
|
||||
|
||||
@@ -657,6 +657,8 @@ interface DownloadState {
|
||||
pendingAddHeaders: string;
|
||||
pendingAddCookies: string;
|
||||
pendingAddMediaUrls: string[];
|
||||
pendingAddBatch: boolean;
|
||||
pendingAddBatchName: string;
|
||||
pendingAddRequestContexts: Record<string, PendingAddRequestContext>;
|
||||
pendingAddRequestVersion: number;
|
||||
selectedPropertiesDownloadId: string | null;
|
||||
@@ -668,7 +670,9 @@ interface DownloadState {
|
||||
headers?: string | null,
|
||||
cookies?: string | null,
|
||||
media?: boolean,
|
||||
cookieScopes?: ExtensionCookieScope[] | null
|
||||
cookieScopes?: ExtensionCookieScope[] | null,
|
||||
batch?: boolean,
|
||||
batchName?: string | null
|
||||
) => void;
|
||||
handleExtensionDownload: (request: ExtensionDownloadRequest) => Promise<void>;
|
||||
deleteModalState: DeleteModalState;
|
||||
@@ -918,6 +922,8 @@ export const useDownloadStore = create<DownloadState>((set, get) => {
|
||||
pendingAddHeaders: '',
|
||||
pendingAddCookies: '',
|
||||
pendingAddMediaUrls: [],
|
||||
pendingAddBatch: false,
|
||||
pendingAddBatchName: '',
|
||||
pendingAddRequestContexts: {},
|
||||
pendingAddRequestVersion: 0,
|
||||
selectedPropertiesDownloadId: null,
|
||||
@@ -937,12 +943,24 @@ export const useDownloadStore = create<DownloadState>((set, get) => {
|
||||
pendingAddHeaders: '',
|
||||
pendingAddCookies: '',
|
||||
pendingAddMediaUrls: [],
|
||||
pendingAddBatch: false,
|
||||
pendingAddBatchName: '',
|
||||
pendingAddRequestContexts: {},
|
||||
// Invalidate any in-flight Add-modal handoff even when the modal is
|
||||
// opened or closed without URLs.
|
||||
pendingAddRequestVersion: state.pendingAddRequestVersion + 1
|
||||
})),
|
||||
openAddModalWithUrls: (urls, referer, filename, headers, cookies, media = false, cookieScopes) => set((state) => {
|
||||
openAddModalWithUrls: (
|
||||
urls,
|
||||
referer,
|
||||
filename,
|
||||
headers,
|
||||
cookies,
|
||||
media = false,
|
||||
cookieScopes,
|
||||
batch = false,
|
||||
batchName
|
||||
) => set((state) => {
|
||||
const isAppending = state.isAddModalOpen && Boolean(state.pendingAddUrls);
|
||||
const existingUrls = isAppending ? state.pendingAddUrls : '';
|
||||
const mergedUrls = existingUrls ? `${existingUrls}\n${urls}` : urls;
|
||||
@@ -950,6 +968,13 @@ export const useDownloadStore = create<DownloadState>((set, get) => {
|
||||
const cleanFilename = filename?.trim() || '';
|
||||
const cleanHeaders = headers?.trim() || '';
|
||||
const cleanCookies = cookies?.trim() || '';
|
||||
// Keep the first modal request's grouping decision stable while later
|
||||
// handoffs append URLs. This avoids moving an already-visible destination
|
||||
// when a second request races with the user's Add-window setup.
|
||||
const nextBatch = isAppending ? state.pendingAddBatch : batch;
|
||||
const nextBatchName = nextBatch
|
||||
? (isAppending ? state.pendingAddBatchName : batchName?.trim() || '')
|
||||
: '';
|
||||
const cleanCookieScopes = cookieScopes
|
||||
?.map(scope => ({
|
||||
url: scope.url.trim(),
|
||||
@@ -994,6 +1019,8 @@ export const useDownloadStore = create<DownloadState>((set, get) => {
|
||||
pendingAddHeaders: cleanHeaders,
|
||||
pendingAddCookies: cleanCookies,
|
||||
pendingAddMediaUrls,
|
||||
pendingAddBatch: nextBatch,
|
||||
pendingAddBatchName: nextBatchName,
|
||||
pendingAddRequestContexts,
|
||||
pendingAddRequestVersion: requestVersion
|
||||
};
|
||||
@@ -1017,7 +1044,9 @@ export const useDownloadStore = create<DownloadState>((set, get) => {
|
||||
headers,
|
||||
cookies,
|
||||
request.media === true,
|
||||
request.media === true ? undefined : request.cookie_scopes
|
||||
request.media === true ? undefined : request.cookie_scopes,
|
||||
request.batch === true && urls.length >= 2,
|
||||
request.batch_name
|
||||
);
|
||||
},
|
||||
setSelectedPropertiesDownloadId: (id) => set({ selectedPropertiesDownloadId: id }),
|
||||
|
||||
Reference in New Issue
Block a user