You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Lidarr/frontend/src/Store/Actions/interactiveImportActions.js

354 lines
10 KiB

import moment from 'moment';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import updateSectionState from 'Utilities/State/updateSectionState';
import naturalExpansion from 'Utilities/String/naturalExpansion';
import { set, update, updateItem } from './baseActions';
import createFetchHandler from './Creators/createFetchHandler';
import createHandleActions from './Creators/createHandleActions';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
//
// Variables
export const section = 'interactiveImport';
const albumsSection = `${section}.albums`;
const trackFilesSection = `${section}.trackFiles`;
let abortCurrentFetchRequest = null;
let abortCurrentRequest = null;
let currentIds = [];
const MAXIMUM_RECENT_FOLDERS = 10;
//
// State
export const defaultState = {
isFetching: false,
isPopulated: false,
isSaving: false,
error: null,
items: [],
pendingChanges: {},
sortKey: 'path',
sortDirection: sortDirections.ASCENDING,
secondarySortKey: 'path',
secondarySortDirection: sortDirections.ASCENDING,
recentFolders: [],
importMode: 'chooseImportMode',
sortPredicates: {
path: function(item, direction) {
const path = item.path;
return naturalExpansion(path.toLowerCase());
},
artist: function(item, direction) {
const artist = item.artist;
return artist ? artist.sortName : '';
},
quality: function(item, direction) {
return item.qualityWeight || 0;
}
},
albums: {
isFetching: false,
isPopulated: false,
error: null,
sortKey: 'albumTitle',
sortDirection: sortDirections.ASCENDING,
items: []
},
trackFiles: {
isFetching: false,
isPopulated: false,
error: null,
sortKey: 'relativePath',
sortDirection: sortDirections.ASCENDING,
items: []
}
};
export const persistState = [
'interactiveImport.sortKey',
'interactiveImport.sortDirection',
'interactiveImport.recentFolders',
'interactiveImport.importMode'
];
//
// Actions Types
export const FETCH_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/fetchInteractiveImportItems';
export const SAVE_INTERACTIVE_IMPORT_ITEM = 'interactiveImport/saveInteractiveImportItem';
export const SET_INTERACTIVE_IMPORT_SORT = 'interactiveImport/setInteractiveImportSort';
export const UPDATE_INTERACTIVE_IMPORT_ITEM = 'interactiveImport/updateInteractiveImportItem';
export const UPDATE_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/updateInteractiveImportItems';
export const CLEAR_INTERACTIVE_IMPORT = 'interactiveImport/clearInteractiveImport';
export const ADD_RECENT_FOLDER = 'interactiveImport/addRecentFolder';
export const REMOVE_RECENT_FOLDER = 'interactiveImport/removeRecentFolder';
export const SET_INTERACTIVE_IMPORT_MODE = 'interactiveImport/setInteractiveImportMode';
export const FETCH_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/fetchInteractiveImportAlbums';
export const SET_INTERACTIVE_IMPORT_ALBUMS_SORT = 'interactiveImport/clearInteractiveImportAlbumsSort';
export const CLEAR_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/clearInteractiveImportAlbums';
export const FETCH_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/fetchInteractiveImportTrackFiles';
export const CLEAR_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/clearInteractiveImportTrackFiles';
//
// Action Creators
export const fetchInteractiveImportItems = createThunk(FETCH_INTERACTIVE_IMPORT_ITEMS);
export const setInteractiveImportSort = createAction(SET_INTERACTIVE_IMPORT_SORT);
export const updateInteractiveImportItem = createAction(UPDATE_INTERACTIVE_IMPORT_ITEM);
export const updateInteractiveImportItems = createAction(UPDATE_INTERACTIVE_IMPORT_ITEMS);
export const saveInteractiveImportItem = createThunk(SAVE_INTERACTIVE_IMPORT_ITEM);
export const clearInteractiveImport = createAction(CLEAR_INTERACTIVE_IMPORT);
export const addRecentFolder = createAction(ADD_RECENT_FOLDER);
export const removeRecentFolder = createAction(REMOVE_RECENT_FOLDER);
export const setInteractiveImportMode = createAction(SET_INTERACTIVE_IMPORT_MODE);
export const fetchInteractiveImportAlbums = createThunk(FETCH_INTERACTIVE_IMPORT_ALBUMS);
export const setInteractiveImportAlbumsSort = createAction(SET_INTERACTIVE_IMPORT_ALBUMS_SORT);
export const clearInteractiveImportAlbums = createAction(CLEAR_INTERACTIVE_IMPORT_ALBUMS);
export const fetchInteractiveImportTrackFiles = createThunk(FETCH_INTERACTIVE_IMPORT_TRACKFILES);
export const clearInteractiveImportTrackFiles = createAction(CLEAR_INTERACTIVE_IMPORT_TRACKFILES);
//
// Action Handlers
export const actionHandlers = handleThunks({
[FETCH_INTERACTIVE_IMPORT_ITEMS]: function(getState, payload, dispatch) {
if (abortCurrentFetchRequest) {
abortCurrentFetchRequest();
abortCurrentFetchRequest = null;
}
if (!payload.downloadId && !payload.folder) {
dispatch(set({ section, error: { message: '`downloadId` or `folder` is required.' } }));
return;
}
dispatch(set({ section, isFetching: true }));
const { request, abortRequest } = createAjaxRequest({
url: '/manualimport',
data: payload
});
abortCurrentFetchRequest = abortRequest;
request.done((data) => {
dispatch(batchActions([
update({ section, data }),
set({
section,
isFetching: false,
isPopulated: true,
error: null
})
]));
});
request.fail((xhr) => {
if (xhr.aborted) {
return;
}
dispatch(set({
section,
isFetching: false,
isPopulated: false,
error: xhr
}));
});
},
[SAVE_INTERACTIVE_IMPORT_ITEM]: function(getState, payload, dispatch) {
if (abortCurrentRequest) {
abortCurrentRequest();
}
dispatch(batchActions([
...currentIds.map((id) => updateItem({
section,
id,
isReprocessing: false,
updateOnly: true
})),
...payload.ids.map((id) => updateItem({
section,
id,
isReprocessing: true,
updateOnly: true
}))
]));
const items = getState()[section].items;
const requestPayload = payload.ids.map((id) => {
const item = items.find((i) => i.id === id);
return {
id,
path: item.path,
artistId: item.artist ? item.artist.id : undefined,
albumId: item.album ? item.album.id : undefined,
albumReleaseId: item.albumReleaseId ? item.albumReleaseId : undefined,
trackIds: (item.tracks || []).map((e) => e.id),
quality: item.quality,
releaseGroup: item.releaseGroup,
indexerFlags: item.indexerFlags,
downloadId: item.downloadId,
additionalFile: item.additionalFile,
replaceExistingFiles: item.replaceExistingFiles,
disableReleaseSwitching: item.disableReleaseSwitching
};
});
const { request, abortRequest } = createAjaxRequest({
method: 'POST',
url: '/manualimport',
contentType: 'application/json',
data: JSON.stringify(requestPayload)
});
abortCurrentRequest = abortRequest;
currentIds = payload.ids;
request.done((data) => {
dispatch(batchActions(
data.map((item) => updateItem({
section,
...item,
isReprocessing: false,
updateOnly: true
}))
));
});
request.fail((xhr) => {
if (xhr.aborted) {
return;
}
dispatch(batchActions(
payload.ids.map((id) => updateItem({
section,
id,
isReprocessing: false,
updateOnly: true
}))
));
});
},
[FETCH_INTERACTIVE_IMPORT_ALBUMS]: createFetchHandler(albumsSection, '/album'),
[FETCH_INTERACTIVE_IMPORT_TRACKFILES]: createFetchHandler(trackFilesSection, '/trackFile')
});
//
// Reducers
export const reducers = createHandleActions({
[UPDATE_INTERACTIVE_IMPORT_ITEM]: (state, { payload }) => {
const id = payload.id;
const newState = Object.assign({}, state);
const items = newState.items;
const index = items.findIndex((item) => item.id === id);
const item = Object.assign({}, items[index], payload);
newState.items = [...items];
newState.items.splice(index, 1, item);
return newState;
},
[UPDATE_INTERACTIVE_IMPORT_ITEMS]: (state, { payload }) => {
const ids = payload.ids;
const newState = Object.assign({}, state);
const items = [...newState.items];
ids.forEach((id) => {
const index = items.findIndex((item) => item.id === id);
const item = Object.assign({}, items[index], payload);
items.splice(index, 1, item);
});
newState.items = items;
return newState;
},
[ADD_RECENT_FOLDER]: function(state, { payload }) {
const folder = payload.folder;
const recentFolder = { folder, lastUsed: moment().toISOString() };
const recentFolders = [...state.recentFolders];
const index = recentFolders.findIndex((r) => r.folder === folder);
if (index > -1) {
recentFolders.splice(index, 1);
}
recentFolders.push(recentFolder);
const sliceIndex = Math.max(recentFolders.length - MAXIMUM_RECENT_FOLDERS, 0);
return Object.assign({}, state, { recentFolders: recentFolders.slice(sliceIndex) });
},
[REMOVE_RECENT_FOLDER]: function(state, { payload }) {
const folder = payload.folder;
const recentFolders = [...state.recentFolders];
const index = recentFolders.findIndex((r) => r.folder === folder);
recentFolders.splice(index, 1);
return Object.assign({}, state, { recentFolders });
},
[CLEAR_INTERACTIVE_IMPORT]: function(state) {
const newState = {
...defaultState,
recentFolders: state.recentFolders,
importMode: state.importMode
};
return newState;
},
[SET_INTERACTIVE_IMPORT_SORT]: createSetClientSideCollectionSortReducer(section),
[SET_INTERACTIVE_IMPORT_MODE]: function(state, { payload }) {
return Object.assign({}, state, { importMode: payload.importMode });
},
[SET_INTERACTIVE_IMPORT_ALBUMS_SORT]: createSetClientSideCollectionSortReducer(albumsSection),
[CLEAR_INTERACTIVE_IMPORT_ALBUMS]: (state) => {
return updateSectionState(state, albumsSection, {
...defaultState.albums
});
},
[CLEAR_INTERACTIVE_IMPORT_TRACKFILES]: (state) => {
return updateSectionState(state, trackFilesSection, {
...defaultState.trackFiles
});
}
}, defaultState, section);