diff --git a/frontend/.eslintrc b/frontend/.eslintrc
index 8593e9f61..21ef60f75 100644
--- a/frontend/.eslintrc
+++ b/frontend/.eslintrc
@@ -268,7 +268,7 @@
"react/jsx-curly-spacing": [2, "never"],
"react/jsx-equals-spacing": [2, "never"],
"react/jsx-indent-props": [2, 2],
- "react/jsx-indent": [2, 2],
+ "react/jsx-indent": [2, 2, { "indentLogicalExpressions": true }],
"react/jsx-key": 2,
"react/jsx-no-bind": [2, { "allowArrowFunctions": true }],
"react/jsx-no-duplicate-props": [2, { "ignoreCase": true }],
diff --git a/frontend/gulp/build.js b/frontend/gulp/build.js
index de2da698f..8d036f92e 100644
--- a/frontend/gulp/build.js
+++ b/frontend/gulp/build.js
@@ -10,8 +10,7 @@ gulp.task('build',
'webpack',
'copyHtml',
'copyFonts',
- 'copyImages',
- 'copyJs'
+ 'copyImages'
)
)
);
diff --git a/frontend/gulp/copy.js b/frontend/gulp/copy.js
index 8d58ac4a4..871c565f6 100644
--- a/frontend/gulp/copy.js
+++ b/frontend/gulp/copy.js
@@ -5,17 +5,6 @@ const cache = require('gulp-cached');
const livereload = require('gulp-livereload');
const paths = require('./helpers/paths.js');
-gulp.task('copyJs', () => {
- return gulp.src(
- [
- path.join(paths.src.root, 'polyfills.js')
- ], { base: paths.src.root })
- .pipe(cache('copyJs'))
- .pipe(print())
- .pipe(gulp.dest(paths.dest.root))
- .pipe(livereload());
-});
-
gulp.task('copyHtml', () => {
return gulp.src(paths.src.html, { base: paths.src.root })
.pipe(cache('copyHtml'))
diff --git a/frontend/gulp/webpack.js b/frontend/gulp/webpack.js
index 56b222935..4414ec4d2 100644
--- a/frontend/gulp/webpack.js
+++ b/frontend/gulp/webpack.js
@@ -14,6 +14,7 @@ const frontendFolder = path.join(__dirname, '..');
const srcFolder = path.join(frontendFolder, 'src');
const isProduction = process.argv.indexOf('--production') > -1;
const isProfiling = isProduction && process.argv.indexOf('--profile') > -1;
+const inlineWebWorkers = true;
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
@@ -124,7 +125,9 @@ const config = {
use: {
loader: 'worker-loader',
options: {
- name: '[name].js'
+ name: '[name].js',
+ inline: inlineWebWorkers,
+ fallback: !inlineWebWorkers
}
}
},
diff --git a/frontend/src/Activity/Queue/RemoveQueueItemsModal.js b/frontend/src/Activity/Queue/RemoveQueueItemsModal.js
index f6efb1b73..2927eb005 100644
--- a/frontend/src/Activity/Queue/RemoveQueueItemsModal.js
+++ b/frontend/src/Activity/Queue/RemoveQueueItemsModal.js
@@ -124,16 +124,16 @@ class RemoveQueueItemsModal extends Component {
{
blacklist &&
-
- Skip Redownload
-
-
+
+ Skip Redownload
+
+
}
diff --git a/frontend/src/Author/Index/Banners/Options/AuthorIndexBannerOptionsModalContent.js b/frontend/src/Author/Index/Banners/Options/AuthorIndexBannerOptionsModalContent.js
index a3b35b892..cf2f1c31a 100644
--- a/frontend/src/Author/Index/Banners/Options/AuthorIndexBannerOptionsModalContent.js
+++ b/frontend/src/Author/Index/Banners/Options/AuthorIndexBannerOptionsModalContent.js
@@ -108,7 +108,7 @@ class AuthorIndexBannerOptionsModalContent extends Component {
return (
- Options
+ Options
diff --git a/frontend/src/Author/Index/Overview/AuthorIndexOverview.js b/frontend/src/Author/Index/Overview/AuthorIndexOverview.js
index 81fb73a11..23ae6deef 100644
--- a/frontend/src/Author/Index/Overview/AuthorIndexOverview.js
+++ b/frontend/src/Author/Index/Overview/AuthorIndexOverview.js
@@ -128,10 +128,10 @@ class AuthorIndexOverview extends Component {
{
status === 'ended' &&
-
+
}
{
!!nextAiring &&
-
+
}
{
diff --git a/frontend/src/Author/NoAuthor.js b/frontend/src/Author/NoAuthor.js
index edbcf70fd..58054b94d 100644
--- a/frontend/src/Author/NoAuthor.js
+++ b/frontend/src/Author/NoAuthor.js
@@ -11,7 +11,7 @@ function NoAuthor(props) {
return (
- All authors are hidden due to the applied filter.
+ All authors are hidden due to the applied filter.
);
diff --git a/frontend/src/Calendar/CalendarPage.js b/frontend/src/Calendar/CalendarPage.js
index 698b2a47f..f49909c0b 100644
--- a/frontend/src/Calendar/CalendarPage.js
+++ b/frontend/src/Calendar/CalendarPage.js
@@ -135,9 +135,9 @@ class CalendarPage extends Component {
>
{
authorError &&
-
- {getErrorMessage(authorError, 'Failed to load author from API')}
-
+
+ {getErrorMessage(authorError, 'Failed to load author from API')}
+
}
{
diff --git a/frontend/src/Calendar/Events/CalendarEvent.js b/frontend/src/Calendar/Events/CalendarEvent.js
index c48f6d1d3..4a336afe2 100644
--- a/frontend/src/Calendar/Events/CalendarEvent.js
+++ b/frontend/src/Calendar/Events/CalendarEvent.js
@@ -94,11 +94,11 @@ class CalendarEvent extends Component {
{
!queueItem && grabbed &&
-
+
}
diff --git a/frontend/src/Components/Filter/CustomFilters/CustomFiltersModalContent.js b/frontend/src/Components/Filter/CustomFilters/CustomFiltersModalContent.js
index fb2c13a12..9f8645dda 100644
--- a/frontend/src/Components/Filter/CustomFilters/CustomFiltersModalContent.js
+++ b/frontend/src/Components/Filter/CustomFilters/CustomFiltersModalContent.js
@@ -24,7 +24,7 @@ function CustomFiltersModalContent(props) {
return (
- Custom Filters
+ Custom Filters
@@ -58,7 +58,7 @@ function CustomFiltersModalContent(props) {
diff --git a/frontend/src/Components/Form/RootFolderSelectInputConnector.js b/frontend/src/Components/Form/RootFolderSelectInputConnector.js
index 928ecea28..ea4cd2ae2 100644
--- a/frontend/src/Components/Form/RootFolderSelectInputConnector.js
+++ b/frontend/src/Components/Form/RootFolderSelectInputConnector.js
@@ -59,7 +59,7 @@ class RootFolderSelectInputConnector extends Component {
//
// Lifecycle
- componentWillMount() {
+ UNSAFE_componentWillMount() {
const {
value,
values,
diff --git a/frontend/src/Components/Loading/LoadingIndicator.js b/frontend/src/Components/Loading/LoadingIndicator.js
index 5f9a15b1a..60f692a45 100644
--- a/frontend/src/Components/Loading/LoadingIndicator.js
+++ b/frontend/src/Components/Loading/LoadingIndicator.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import styles from './LoadingIndicator.css';
-function LoadingIndicator({ className, size }) {
+function LoadingIndicator({ className, rippleClassName, size }) {
const sizeInPx = `${size}px`;
const width = sizeInPx;
const height = sizeInPx;
@@ -17,17 +17,17 @@ function LoadingIndicator({ className, size }) {
style={{ width, height }}
>
@@ -37,11 +37,13 @@ function LoadingIndicator({ className, size }) {
LoadingIndicator.propTypes = {
className: PropTypes.string,
+ rippleClassName: PropTypes.string,
size: PropTypes.number
};
LoadingIndicator.defaultProps = {
className: styles.loading,
+ rippleClassName: styles.ripple,
size: 50
};
diff --git a/frontend/src/Components/Menu/FilterMenuContent.js b/frontend/src/Components/Menu/FilterMenuContent.js
index 7463e2c9e..ce098ee62 100644
--- a/frontend/src/Components/Menu/FilterMenuContent.js
+++ b/frontend/src/Components/Menu/FilterMenuContent.js
@@ -61,7 +61,7 @@ class FilterMenuContent extends Component {
{
showCustomFilters &&
}
diff --git a/frontend/src/Components/Modal/ConfirmModal.js b/frontend/src/Components/Modal/ConfirmModal.js
index 5bb783d43..bd80ce7b8 100644
--- a/frontend/src/Components/Modal/ConfirmModal.js
+++ b/frontend/src/Components/Modal/ConfirmModal.js
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
-import React from 'react';
+import React, { useEffect } from 'react';
import { kinds, sizes } from 'Helpers/Props';
+import keyboardShortcuts from 'Components/keyboardShortcuts';
import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton';
import Modal from 'Components/Modal/Modal';
@@ -21,9 +22,19 @@ function ConfirmModal(props) {
hideCancelButton,
isSpinning,
onConfirm,
- onCancel
+ onCancel,
+ bindShortcut,
+ unbindShortcut
} = props;
+ useEffect(() => {
+ if (isOpen) {
+ bindShortcut('enter', onConfirm);
+ } else {
+ unbindShortcut('enter', onConfirm);
+ }
+ }, [onConfirm]);
+
return (
+
-
- {children}
-
+
+ {children}
+
+
- ,
+ ,
this._node
);
}
diff --git a/frontend/src/Components/Page/Header/AuthorSearchInput.css b/frontend/src/Components/Page/Header/AuthorSearchInput.css
index b4710f443..77ec088d5 100644
--- a/frontend/src/Components/Page/Header/AuthorSearchInput.css
+++ b/frontend/src/Components/Page/Header/AuthorSearchInput.css
@@ -3,6 +3,18 @@
align-items: center;
}
+.loading {
+ margin-top: 18px;
+ margin-bottom: 18px;
+ text-align: center;
+}
+
+.ripple {
+ composes: ripple from '~Components/Loading/LoadingIndicator.css';
+
+ border: 2px solid $toolbarColor;
+}
+
.input {
margin-left: 8px;
width: 200px;
diff --git a/frontend/src/Components/Page/Header/AuthorSearchInput.js b/frontend/src/Components/Page/Header/AuthorSearchInput.js
index cb0840517..7174a198b 100644
--- a/frontend/src/Components/Page/Header/AuthorSearchInput.js
+++ b/frontend/src/Components/Page/Header/AuthorSearchInput.js
@@ -74,7 +74,11 @@ class AuthorSearchInput extends Component {
if (item.type === LOADING_TYPE) {
return (
-
+
);
}
diff --git a/frontend/src/Components/Page/Header/fuse.worker.js b/frontend/src/Components/Page/Header/fuse.worker.js
index 936f8e389..6a0799ca9 100644
--- a/frontend/src/Components/Page/Header/fuse.worker.js
+++ b/frontend/src/Components/Page/Header/fuse.worker.js
@@ -6,7 +6,6 @@ const fuseOptions = {
threshold: 0.3,
location: 0,
distance: 100,
- maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
'authorName',
diff --git a/frontend/src/Components/Scroller/Scroller.js b/frontend/src/Components/Scroller/Scroller.js
index 22fae04c6..603e27cc6 100644
--- a/frontend/src/Components/Scroller/Scroller.js
+++ b/frontend/src/Components/Scroller/Scroller.js
@@ -17,12 +17,18 @@ class Scroller extends Component {
componentDidMount() {
const {
+ scrollDirection,
+ autoFocus,
scrollTop
} = this.props;
if (this.props.scrollTop != null) {
this._scroller.scrollTop = scrollTop;
}
+
+ if (autoFocus && scrollDirection !== scrollDirections.NONE) {
+ this._scroller.focus({ preventScroll: true });
+ }
}
//
@@ -58,6 +64,7 @@ class Scroller extends Component {
styles[scrollDirection],
autoScroll && styles.autoScroll
)}
+ tabIndex={-1}
{...otherProps}
>
{children}
@@ -70,6 +77,7 @@ class Scroller extends Component {
Scroller.propTypes = {
className: PropTypes.string,
scrollDirection: PropTypes.oneOf(scrollDirections.all).isRequired,
+ autoFocus: PropTypes.bool.isRequired,
autoScroll: PropTypes.bool.isRequired,
scrollTop: PropTypes.number,
children: PropTypes.node,
@@ -79,6 +87,7 @@ Scroller.propTypes = {
Scroller.defaultProps = {
scrollDirection: scrollDirections.VERTICAL,
+ autoFocus: true,
autoScroll: true,
registerScroller: () => {}
};
diff --git a/frontend/src/Components/Table/TableOptions/TableOptionsModal.js b/frontend/src/Components/Table/TableOptions/TableOptionsModal.js
index 2a36668fe..64b2dcb6a 100644
--- a/frontend/src/Components/Table/TableOptions/TableOptionsModal.js
+++ b/frontend/src/Components/Table/TableOptions/TableOptionsModal.js
@@ -136,7 +136,7 @@ class TableOptionsModal extends Component {
isOpen ?
- Table Options
+ Table Options
@@ -231,7 +231,7 @@ class TableOptionsModal extends Component {
:
diff --git a/frontend/src/Components/keyboardShortcuts.js b/frontend/src/Components/keyboardShortcuts.js
index a99a0dc7f..b75fe6cac 100644
--- a/frontend/src/Components/keyboardShortcuts.js
+++ b/frontend/src/Components/keyboardShortcuts.js
@@ -8,6 +8,16 @@ export const shortcuts = {
name: 'Open This Modal'
},
+ CLOSE_MODAL: {
+ key: 'Esc',
+ name: 'Close Current Modal'
+ },
+
+ ACCEPT_CONFIRM_MODAL: {
+ key: 'Enter',
+ name: 'Accept Confirmation Modal'
+ },
+
AUTHOR_SEARCH_INPUT: {
key: 's',
name: 'Focus Search Box'
diff --git a/frontend/src/InteractiveImport/Confirmation/ConfirmImportModalContent.js b/frontend/src/InteractiveImport/Confirmation/ConfirmImportModalContent.js
index 3e1e9966f..58755c2bc 100644
--- a/frontend/src/InteractiveImport/Confirmation/ConfirmImportModalContent.js
+++ b/frontend/src/InteractiveImport/Confirmation/ConfirmImportModalContent.js
@@ -72,9 +72,9 @@ class ConfirmImportModalContent extends Component {
{
!isFetching && isPopulated &&
-
- Are you sure?
-
+
+ Are you sure?
+
}
diff --git a/frontend/src/InteractiveSearch/InteractiveSearch.js b/frontend/src/InteractiveSearch/InteractiveSearch.js
index 9a4277577..2359acda3 100644
--- a/frontend/src/InteractiveSearch/InteractiveSearch.js
+++ b/frontend/src/InteractiveSearch/InteractiveSearch.js
@@ -156,7 +156,7 @@ function InteractiveSearch(props) {
{
totalReleasesCount !== items.length && !!items.length ?
- Some results are hidden by the applied filter
+ Some results are hidden by the applied filter
:
null
}
diff --git a/frontend/src/Search/Author/AddNewAuthorSearchResult.js b/frontend/src/Search/Author/AddNewAuthorSearchResult.js
index fd74959ec..86054dafc 100644
--- a/frontend/src/Search/Author/AddNewAuthorSearchResult.js
+++ b/frontend/src/Search/Author/AddNewAuthorSearchResult.js
@@ -126,7 +126,7 @@ class AddNewAuthorSearchResult extends Component {
{
!!disambiguation &&
- ({disambiguation})
+ ({disambiguation})
}
{
diff --git a/frontend/src/Search/Book/AddNewBookSearchResult.js b/frontend/src/Search/Book/AddNewBookSearchResult.js
index b58567fa4..95faef77a 100644
--- a/frontend/src/Search/Book/AddNewBookSearchResult.js
+++ b/frontend/src/Search/Book/AddNewBookSearchResult.js
@@ -103,12 +103,12 @@ class AddNewBookSearchResult extends Component {
{
!isSmallScreen &&
-
+
}
@@ -117,7 +117,7 @@ class AddNewBookSearchResult extends Component {
{
!!disambiguation &&
-
({disambiguation})
+
({disambiguation})
}
{
diff --git a/frontend/src/Settings/General/ProxySettings.js b/frontend/src/Settings/General/ProxySettings.js
index 5febc6b3a..a20bfb184 100644
--- a/frontend/src/Settings/General/ProxySettings.js
+++ b/frontend/src/Settings/General/ProxySettings.js
@@ -44,90 +44,90 @@ function ProxySettings(props) {
{
proxyEnabled.value &&
-
-
- Proxy Type
-
-
-
-
-
- Hostname
-
-
-
-
-
- Port
-
-
-
-
-
- Username
-
-
-
-
-
- Password
-
-
-
-
-
- Ignored Addresses
-
-
-
-
-
- Bypass Proxy for Local Addresses
-
-
-
-
+
+
+ Proxy Type
+
+
+
+
+
+ Hostname
+
+
+
+
+
+ Port
+
+
+
+
+
+ Username
+
+
+
+
+
+ Password
+
+
+
+
+
+ Ignored Addresses
+
+
+
+
+
+ Bypass Proxy for Local Addresses
+
+
+
+
}
);
diff --git a/frontend/src/Settings/General/SecuritySettings.js b/frontend/src/Settings/General/SecuritySettings.js
index bbcf7194e..ae6bb2b54 100644
--- a/frontend/src/Settings/General/SecuritySettings.js
+++ b/frontend/src/Settings/General/SecuritySettings.js
@@ -93,32 +93,32 @@ class SecuritySettings extends Component {
{
authenticationEnabled &&
-
- Username
-
-
-
+
+ Username
+
+
+
}
{
authenticationEnabled &&
-
- Password
-
-
-
+
+ Password
+
+
+
}
diff --git a/frontend/src/Settings/General/UpdateSettings.js b/frontend/src/Settings/General/UpdateSettings.js
index d054caa4b..f78c1c71e 100644
--- a/frontend/src/Settings/General/UpdateSettings.js
+++ b/frontend/src/Settings/General/UpdateSettings.js
@@ -72,58 +72,58 @@ function UpdateSettings(props) {
{
!isWindows &&
-
-
- Automatic
-
-
-
-
-
- Mechanism
-
-
-
-
- {
- updateMechanism.value === 'script' &&
+
- Script Path
+ Automatic
- }
-
+
+
+ Mechanism
+
+
+
+
+ {
+ updateMechanism.value === 'script' &&
+
+ Script Path
+
+
+
+ }
+
}
);
diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
index f107aa123..093c7ef29 100644
--- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
+++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
@@ -199,23 +199,23 @@ function EditImportListModalContent(props) {
{
!!fields && !!fields.length &&
-
- {
- fields.map((field) => {
- return (
-
- );
- })
- }
-
+
+ {
+ fields.map((field) => {
+ return (
+
+ );
+ })
+ }
+
}
diff --git a/frontend/src/Settings/Indexers/Indexers/Indexer.js b/frontend/src/Settings/Indexers/Indexers/Indexer.js
index 9269f8532..8d7e5719d 100644
--- a/frontend/src/Settings/Indexers/Indexers/Indexer.js
+++ b/frontend/src/Settings/Indexers/Indexers/Indexer.js
@@ -96,12 +96,12 @@ class Indexer extends Component {
{
!enableRss && !enableAutomaticSearch && !enableInteractiveSearch &&
-
+
}
diff --git a/frontend/src/Settings/Metadata/MetadataProvider/MetadataProvider.js b/frontend/src/Settings/Metadata/MetadataProvider/MetadataProvider.js
index ee8eb8004..c8ca73918 100644
--- a/frontend/src/Settings/Metadata/MetadataProvider/MetadataProvider.js
+++ b/frontend/src/Settings/Metadata/MetadataProvider/MetadataProvider.js
@@ -43,23 +43,23 @@ function MetadataProvider(props) {
- Restore
+ Restore
diff --git a/frontend/src/System/Events/LogsTable.js b/frontend/src/System/Events/LogsTable.js
index ce6d0c995..26f639c65 100644
--- a/frontend/src/System/Events/LogsTable.js
+++ b/frontend/src/System/Events/LogsTable.js
@@ -76,45 +76,45 @@ function LogsTable(props) {
{
isFetching && !isPopulated &&
-
+
}
{
isPopulated && !error && !items.length &&
-
- No logs found
-
+
+ No logs found
+
}
{
isPopulated && !error && !!items.length &&
-
-
-
- {
- items.map((item) => {
- return (
-
- );
- })
- }
-
-
+
+
+
+ {
+ items.map((item) => {
+ return (
+
+ );
+ })
+ }
+
+
-
-
+
+
}
diff --git a/frontend/src/System/Tasks/Queued/QueuedTasks.js b/frontend/src/System/Tasks/Queued/QueuedTasks.js
index a2fd526fa..e856df532 100644
--- a/frontend/src/System/Tasks/Queued/QueuedTasks.js
+++ b/frontend/src/System/Tasks/Queued/QueuedTasks.js
@@ -54,27 +54,27 @@ function QueuedTasks(props) {
);
diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js
index e481d3781..1176db157 100644
--- a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js
+++ b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js
@@ -49,27 +49,27 @@ function ScheduledTasks(props) {
);
diff --git a/frontend/src/index.html b/frontend/src/index.html
index 141ba046f..c7990e278 100644
--- a/frontend/src/index.html
+++ b/frontend/src/index.html
@@ -79,6 +79,5 @@