Misc UI Fixes

pull/6/head
Qstick 7 years ago
parent 52f4f1de03
commit f3e55a236c

@ -6,7 +6,6 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
import { icons } from 'Helpers/Props';
import episodeEntities from 'Episode/episodeEntities';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
@ -100,7 +99,9 @@ class Queue extends Component {
isPopulated,
error,
items,
isAlbumsFetching,
isAlbumsPopulated,
episodesError,
columns,
totalRecords,
isGrabbing,
@ -118,8 +119,9 @@ class Queue extends Component {
isPendingSelected
} = this.state;
const isRefreshing = isFetching || isCheckForFinishedDownloadExecuting;
const isRefreshing = isFetching || isAlbumsFetching || isCheckForFinishedDownloadExecuting;
const isAllPopulated = isPopulated && (isAlbumsPopulated || !items.length);
const hasError = error || episodesError;
const selectedCount = this.getSelectedIds().length;
const disableSelectedActions = selectedCount === 0;
@ -161,21 +163,21 @@ class Queue extends Component {
}
{
!isRefreshing && error &&
!isRefreshing && hasError &&
<div>
Failed to load Queue
</div>
}
{
isAllPopulated && !error && !items.length &&
isPopulated && !hasError && !items.length &&
<div>
Queue is empty
</div>
}
{
isAllPopulated && !error && !!items.length &&
isAllPopulated && !hasError && !!items.length &&
<div>
<Table
columns={columns}
@ -191,8 +193,7 @@ class Queue extends Component {
return (
<QueueRowConnector
key={item.id}
episodeId={item.album.id}
episodeEntity={episodeEntities.QUEUE_EPISODES}
episodeId={item.albumId}
isSelected={selectedState[item.id]}
columns={columns}
{...item}
@ -229,7 +230,9 @@ Queue.propTypes = {
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
isAlbumsFetching: PropTypes.bool.isRequired,
isAlbumsPopulated: PropTypes.bool.isRequired,
episodesError: PropTypes.object,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
totalRecords: PropTypes.number,
isGrabbing: PropTypes.bool.isRequired,

@ -4,24 +4,27 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import selectUniqueIds from 'Utilities/Object/selectUniqueIds';
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
import { executeCommand } from 'Store/Actions/commandActions';
import * as queueActions from 'Store/Actions/queueActions';
import { clearEpisodes } from 'Store/Actions/episodeActions';
import { fetchEpisodes, clearEpisodes } from 'Store/Actions/episodeActions';
import * as commandNames from 'Commands/commandNames';
import Queue from './Queue';
function createMapStateToProps() {
return createSelector(
(state) => state.episodes,
(state) => state.queue.paged,
(state) => state.queue.queueEpisodes,
createCommandsSelector(),
(queue, queueEpisodes, commands) => {
(episodes, queue, commands) => {
const isCheckForFinishedDownloadExecuting = _.some(commands, { name: commandNames.CHECK_FOR_FINISHED_DOWNLOAD });
return {
isAlbumsFetching: episodes.isFetching,
isAlbumsPopulated: episodes.isPopulated,
episodesError: episodes.error,
isCheckForFinishedDownloadExecuting,
isAlbumsPopulated: queueEpisodes.isPopulated,
...queue
};
}
@ -30,6 +33,7 @@ function createMapStateToProps() {
const mapDispatchToProps = {
...queueActions,
fetchEpisodes,
clearEpisodes,
executeCommand
};
@ -45,14 +49,9 @@ class QueueConnector extends Component {
componentDidUpdate(prevProps) {
if (hasDifferentItems(prevProps.items, this.props.items)) {
const episodes = _.uniqBy(_.reduce(this.props.items, (result, item) => {
result.push(item.album);
const albumIds = selectUniqueIds(this.props.items, 'albumId');
this.props.fetchEpisodes({ albumIds });
return result;
}, []), ({ id }) => id);
this.props.clearEpisodes();
this.props.setQueueEpisodes({ episodes });
}
}
@ -143,9 +142,9 @@ QueueConnector.propTypes = {
setQueueSort: PropTypes.func.isRequired,
setQueueTableOption: PropTypes.func.isRequired,
clearQueue: PropTypes.func.isRequired,
setQueueEpisodes: PropTypes.func.isRequired,
grabQueueItems: PropTypes.func.isRequired,
removeQueueItems: PropTypes.func.isRequired,
fetchEpisodes: PropTypes.func.isRequired,
clearEpisodes: PropTypes.func.isRequired,
executeCommand: PropTypes.func.isRequired
};

@ -63,7 +63,6 @@ class QueueRow extends Component {
const {
id,
downloadId,
episodeEntity,
title,
status,
trackedDownloadStatus,
@ -161,7 +160,6 @@ class QueueRow extends Component {
episodeId={episode.id}
artistId={series.id}
trackFileId={episode.trackFileId}
episodeEntity={episodeEntity}
episodeTitle={episode.title}
showOpenArtistButton={true}
/>
@ -295,7 +293,6 @@ class QueueRow extends Component {
QueueRow.propTypes = {
id: PropTypes.number.isRequired,
downloadId: PropTypes.string,
episodeEntity: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
trackedDownloadStatus: PropTypes.string,

@ -67,7 +67,6 @@ class QueueRowConnector extends Component {
QueueRowConnector.propTypes = {
id: PropTypes.number.isRequired,
episodeEntity: PropTypes.string.isRequired,
episode: PropTypes.object,
grabQueueItem: PropTypes.func.isRequired,
removeQueueItem: PropTypes.func.isRequired

@ -340,29 +340,6 @@ class PageSidebar extends Component {
this._touchStartY = touchStartY;
}
onTouchEnd = (event) => {
const touches = event.changedTouches;
const currentTouch = touches[0].pageX;
if (!this._touchStartX) {
return;
}
if (currentTouch > this._touchStartX && currentTouch > 50) {
this._setSidebarTransform(true, 'none');
} else if (currentTouch < this._touchStartX && currentTouch < 80) {
this._setSidebarTransform(false, 'transform 50ms ease-in-out');
} else {
this._setSidebarTransform(this.props.isSidebarVisible);
}
this._touchStartX = null;
}
onTouchCancel = (event) => {
this._touchStartX = null;
}
onTouchMove = (event) => {
const touches = event.touches;
const currentTouchX = touches[0].pageX;
@ -393,6 +370,31 @@ class PageSidebar extends Component {
});
}
onTouchEnd = (event) => {
const touches = event.changedTouches;
const currentTouch = touches[0].pageX;
if (!this._touchStartX) {
return;
}
if (currentTouch > this._touchStartX && currentTouch > 50) {
this._setSidebarTransform(true, 'none');
} else if (currentTouch < this._touchStartX && currentTouch < 80) {
this._setSidebarTransform(false, 'transform 50ms ease-in-out');
} else {
this._setSidebarTransform(this.props.isSidebarVisible);
}
this._touchStartX = null;
this._touchStartY = null;
}
onTouchCancel = (event) => {
this._touchStartX = null;
this._touchStartY = null;
}
onItemPress = () => {
this.props.onSidebarVisibleChange(false);
}

@ -1,13 +1,13 @@
export const CALENDAR = 'calendar';
export const EPISODES = 'episodes';
export const QUEUE_EPISODES = 'queue.queueEpisodes';
export const INTERACTIVE_IMPORT = 'interactiveImport.interactiveImportAlbums';
export const WANTED_CUTOFF_UNMET = 'wanted.cutoffUnmet';
export const WANTED_MISSING = 'wanted.missing';
export default {
CALENDAR,
EPISODES,
QUEUE_EPISODES,
INTERACTIVE_IMPORT,
WANTED_CUTOFF_UNMET,
WANTED_MISSING
};

@ -1,11 +1,14 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import connectSection from 'Store/connectSection';
import { createSelector } from 'reselect';
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
import { fetchEpisodes, setEpisodesSort, clearEpisodes } from 'Store/Actions/episodeActions';
import connectSection from 'Store/connectSection';
import {
updateInteractiveImportItem,
fetchInteractiveImportAlbums,
setInteractiveImportAlbumsSort,
clearInteractiveImportAlbums
} from 'Store/Actions/interactiveImportActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import SelectAlbumModalContent from './SelectAlbumModalContent';
@ -19,9 +22,9 @@ function createMapStateToProps() {
}
const mapDispatchToProps = {
fetchEpisodes,
setEpisodesSort,
clearEpisodes,
fetchInteractiveImportAlbums,
setInteractiveImportAlbumsSort,
clearInteractiveImportAlbums,
updateInteractiveImportItem
};
@ -35,18 +38,22 @@ class SelectAlbumModalContentConnector extends Component {
artistId
} = this.props;
this.props.fetchEpisodes({ artistId });
this.props.fetchInteractiveImportAlbums({ artistId });
}
componentWillUnmount() {
// This clears the albums for the queue and hides the queue
// We'll need another place to store albums for manual import
this.props.clearEpisodes();
this.props.clearInteractiveImportAlbums();
}
//
// Listeners
onSortPress = (sortKey, sortDirection) => {
this.props.setInteractiveImportAlbumsSort({ sortKey, sortDirection });
}
onAlbumSelect = (albumId) => {
const album = _.find(this.props.items, { id: albumId });
@ -78,17 +85,17 @@ SelectAlbumModalContentConnector.propTypes = {
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
artistId: PropTypes.number.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
fetchEpisodes: PropTypes.func.isRequired,
setEpisodesSort: PropTypes.func.isRequired,
clearEpisodes: PropTypes.func.isRequired,
fetchInteractiveImportAlbums: PropTypes.func.isRequired,
setInteractiveImportAlbumsSort: PropTypes.func.isRequired,
clearInteractiveImportAlbums: PropTypes.func.isRequired,
updateInteractiveImportItem: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default connectSection(
createMapStateToProps,
mapDispatchToProps,
undefined,
undefined,
{ section: 'episodes' }
)(SelectAlbumModalContentConnector);
createMapStateToProps,
mapDispatchToProps,
undefined,
undefined,
{ section: 'interactiveImport.interactiveImportAlbums' }
)(SelectAlbumModalContentConnector);

@ -163,7 +163,6 @@ export const SET_QUEUE_SORT = 'SET_QUEUE_SORT';
export const SET_QUEUE_TABLE_OPTION = 'SET_QUEUE_TABLE_OPTION';
export const CLEAR_QUEUE = 'CLEAR_QUEUE';
export const SET_QUEUE_EPISODES = 'SET_QUEUE_EPISODES';
export const GRAB_QUEUE_ITEM = 'GRAB_QUEUE_ITEM';
export const GRAB_QUEUE_ITEMS = 'GRAB_QUEUE_ITEMS';
export const REMOVE_QUEUE_ITEM = 'REMOVE_QUEUE_ITEM';
@ -398,6 +397,10 @@ export const ADD_RECENT_FOLDER = 'ADD_RECENT_FOLDER';
export const REMOVE_RECENT_FOLDER = 'REMOVE_RECENT_FOLDER';
export const SET_INTERACTIVE_IMPORT_MODE = 'SET_INTERACTIVE_IMPORT_MODE';
export const FETCH_INTERACTIVE_IMPORT_ALBUMS = 'FETCH_INTERACTIVE_IMPORT_ALBUMS';
export const SET_INTERACTIVE_IMPORT_ALBUMS_SORT = 'SET_INTERACTIVE_IMPORT_ALBUMS_SORT';
export const CLEAR_INTERACTIVE_IMPORT_ALBUMS = 'CLEAR_INTERACTIVE_IMPORT_ALBUMS';
//
// Root Folders

@ -1,5 +1,6 @@
import $ from 'jquery';
import { batchActions } from 'redux-batched-actions';
import createFetchHandler from './Creators/createFetchHandler';
import * as types from './actionTypes';
import { set, update } from './baseActions';
@ -42,7 +43,9 @@ const interactiveImportActionHandlers = {
}));
});
};
}
},
[types.FETCH_INTERACTIVE_IMPORT_ALBUMS]: createFetchHandler('interactiveImportAlbums', '/album')
};
export default interactiveImportActionHandlers;

@ -9,3 +9,7 @@ export const clearInteractiveImport = createAction(types.CLEAR_INTERACTIVE_IMPOR
export const addRecentFolder = createAction(types.ADD_RECENT_FOLDER);
export const removeRecentFolder = createAction(types.REMOVE_RECENT_FOLDER);
export const setInteractiveImportMode = createAction(types.SET_INTERACTIVE_IMPORT_MODE);
export const fetchInteractiveImportAlbums = interactiveImportActionHandlers[types.FETCH_INTERACTIVE_IMPORT_ALBUMS];
export const setInteractiveImportAlbumsSort = createAction(types.SET_INTERACTIVE_IMPORT_ALBUMS_SORT);
export const clearInteractiveImportAlbums = createAction(types.CLEAR_INTERACTIVE_IMPORT_ALBUMS);

@ -17,7 +17,6 @@ export const setQueueSort = queueActionHandlers[types.SET_QUEUE_SORT];
export const setQueueTableOption = createAction(types.SET_QUEUE_TABLE_OPTION);
export const clearQueue = createAction(types.CLEAR_QUEUE);
export const setQueueEpisodes = createAction(types.SET_QUEUE_EPISODES);
export const grabQueueItem = queueActionHandlers[types.GRAB_QUEUE_ITEM];
export const grabQueueItems = queueActionHandlers[types.GRAB_QUEUE_ITEMS];
export const removeQueueItem = queueActionHandlers[types.REMOVE_QUEUE_ITEM];

@ -1,10 +1,12 @@
import _ from 'lodash';
import moment from 'moment';
import { handleActions } from 'redux-actions';
import updateSectionState from 'Utilities/State/updateSectionState';
import * as types from 'Store/Actions/actionTypes';
import { sortDirections } from 'Helpers/Props';
import createSetReducer from './Creators/createSetReducer';
import createUpdateReducer from './Creators/createUpdateReducer';
import createReducers from './Creators/createReducers';
import createSetClientSideCollectionSortReducer from './Creators/createSetClientSideCollectionSortReducer';
export const defaultState = {
@ -26,6 +28,15 @@ export const defaultState = {
quality: function(item, direction) {
return item.quality.qualityWeight;
}
},
interactiveImportAlbums: {
isFetching: false,
isPopulated: false,
error: null,
sortKey: 'albumTitle',
sortDirection: sortDirections.DESCENDING,
items: []
}
};
@ -35,11 +46,12 @@ export const persistState = [
];
const reducerSection = 'interactiveImport';
const episodesSection = 'interactiveImportAlbums';
const interactiveImportReducers = handleActions({
[types.SET]: createSetReducer(reducerSection),
[types.UPDATE]: createUpdateReducer(reducerSection),
[types.SET]: createReducers([reducerSection, episodesSection], createSetReducer),
[types.UPDATE]: createReducers([reducerSection, episodesSection], createUpdateReducer),
[types.UPDATE_INTERACTIVE_IMPORT_ITEM]: (state, { payload }) => {
const id = payload.id;
@ -90,6 +102,16 @@ const interactiveImportReducers = handleActions({
[types.SET_INTERACTIVE_IMPORT_MODE]: function(state, { payload }) {
return Object.assign({}, state, { importMode: payload.importMode });
},
[types.SET_INTERACTIVE_IMPORT_ALBUMS_SORT]: createSetClientSideCollectionSortReducer(episodesSection),
[types.CLEAR_INTERACTIVE_IMPORT_ALBUMS]: (state) => {
const section = episodesSection;
return updateSectionState(state, section, {
...defaultState.interactiveImportAlbums
});
}
}, defaultState);

@ -135,25 +135,7 @@ const queueReducers = handleActions({
isPopulated: false,
error: null,
items: []
}),
[types.SET_QUEUE_EPISODES]: function(state, { payload }) {
const section = 'queueEpisodes';
return updateSectionState(state, section, {
isPopulated: true,
items: payload.episodes
});
},
[types.CLEAR_EPISODES]: (state) => {
const section = 'queueEpisodes';
return updateSectionState(state, section, {
isPopulated: false,
items: []
});
}
})
}, defaultState);

@ -97,8 +97,8 @@ function sort(items, state) {
function createClientSideCollectionSelector() {
return createSelector(
(state, { section }) => state[section],
(state, { uiSection }) => state[uiSection],
(state, { section }) => _.get(state, section),
(state, { uiSection }) => _.get(state, uiSection),
(sectionState, uiSectionState = {}) => {
const state = Object.assign({}, sectionState, uiSectionState);

@ -23,7 +23,7 @@ class MoreInfo extends Component {
<DescriptionListItemTitle>Wiki</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://github.com/Lidarr/Lidarr/Wiki">wiki.lidarr.audio</Link>
<Link to="https://github.com/lidarr/Lidarr/wiki">wiki.lidarr.audio</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Reddit</DescriptionListItemTitle>
@ -48,12 +48,12 @@ class MoreInfo extends Component {
<DescriptionListItemTitle>Source</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://github.com/Lidarr/Lidarr/">github.com/Lidarr/Lidarr</Link>
<Link to="https://github.com/lidarr/Lidarr/">github.com/Lidarr/Lidarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Feature Requests</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://github.com/Lidarr/Lidarr/issues">github.com/Lidarr/Lidarr/issues</Link>
<Link to="https://github.com/lidarr/Lidarr/issues">github.com/Lidarr/Lidarr/issues</Link>
</DescriptionListItemDescription>
</DescriptionList>

@ -29,10 +29,10 @@ namespace Lidarr.Api.V3.Queue
private PagingResource<QueueResource> GetQueue(PagingResource<QueueResource> pagingResource)
{
var pagingSpec = pagingResource.MapToPagingSpec<QueueResource, NzbDrone.Core.Queue.Queue>("timeleft", SortDirection.Ascending);
var includeSeries = Request.GetBooleanQueryParameter("includeSeries");
var includeEpisode = Request.GetBooleanQueryParameter("includeEpisode");
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAlbum = Request.GetBooleanQueryParameter("includeAlbum");
return ApplyToPage(GetQueue, pagingSpec, (q) => MapToResource(q, includeSeries, includeEpisode));
return ApplyToPage(GetQueue, pagingSpec, (q) => MapToResource(q, includeArtist, includeAlbum));
}
private PagingSpec<NzbDrone.Core.Queue.Queue> GetQueue(PagingSpec<NzbDrone.Core.Queue.Queue> pagingSpec)

Loading…
Cancel
Save