mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Add eslint rules for sonar issues
This commit is contained in:
parent
2690b90d84
commit
19f416580c
12 changed files with 45 additions and 30 deletions
16
.eslintrc.js
16
.eslintrc.js
|
@ -56,6 +56,7 @@ module.exports = {
|
||||||
'no-return-assign': ['error'],
|
'no-return-assign': ['error'],
|
||||||
'no-return-await': ['error'],
|
'no-return-await': ['error'],
|
||||||
'no-sequences': ['error', { 'allowInParentheses': false }],
|
'no-sequences': ['error', { 'allowInParentheses': false }],
|
||||||
|
'no-shadow': ['error'],
|
||||||
'no-trailing-spaces': ['error'],
|
'no-trailing-spaces': ['error'],
|
||||||
'@babel/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
'@babel/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
||||||
'no-useless-constructor': ['error'],
|
'no-useless-constructor': ['error'],
|
||||||
|
@ -70,12 +71,15 @@ module.exports = {
|
||||||
'space-before-blocks': ['error'],
|
'space-before-blocks': ['error'],
|
||||||
'space-infix-ops': 'error',
|
'space-infix-ops': 'error',
|
||||||
'yoda': 'error',
|
'yoda': 'error',
|
||||||
'@typescript-eslint/no-shadow': 'error',
|
|
||||||
|
|
||||||
'react/jsx-filename-extension': ['error', { 'extensions': ['.jsx', '.tsx'] }],
|
'react/jsx-filename-extension': ['error', { 'extensions': ['.jsx', '.tsx'] }],
|
||||||
|
'react/jsx-no-bind': ['error'],
|
||||||
|
'react/jsx-no-constructed-context-values': ['error'],
|
||||||
|
'react/no-array-index-key': ['error'],
|
||||||
|
|
||||||
'sonarjs/cognitive-complexity': ['warn'],
|
'sonarjs/no-inverted-boolean-check': ['error'],
|
||||||
// TODO: Enable the following rules and fix issues
|
// TODO: Enable the following rules and fix issues
|
||||||
|
'sonarjs/cognitive-complexity': ['off'],
|
||||||
'sonarjs/no-duplicate-string': ['off']
|
'sonarjs/no-duplicate-string': ['off']
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
|
@ -255,8 +259,14 @@ module.exports = {
|
||||||
'plugin:jsx-a11y/recommended'
|
'plugin:jsx-a11y/recommended'
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
|
// Use TypeScript equivalent rules when required
|
||||||
|
'no-shadow': ['off'],
|
||||||
|
'@typescript-eslint/no-shadow': ['error'],
|
||||||
'no-useless-constructor': ['off'],
|
'no-useless-constructor': ['off'],
|
||||||
'@typescript-eslint/no-useless-constructor': ['error']
|
'@typescript-eslint/no-useless-constructor': ['error'],
|
||||||
|
|
||||||
|
'max-params': ['error', 7],
|
||||||
|
'sonarjs/cognitive-complexity': ['warn']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -90,8 +90,8 @@ const GenresItemsContainer: FC<GenresItemsContainerProps> = ({
|
||||||
<h1>{globalize.translate('MessageNothingHere')}</h1>
|
<h1>{globalize.translate('MessageNothingHere')}</h1>
|
||||||
<p>{globalize.translate('MessageNoGenresAvailable')}</p>
|
<p>{globalize.translate('MessageNoGenresAvailable')}</p>
|
||||||
</div>
|
</div>
|
||||||
) : items.map((item, index) => (
|
) : items.map(item => (
|
||||||
<div key={index} className='verticalSection'>
|
<div key={item.Id} className='verticalSection'>
|
||||||
<div
|
<div
|
||||||
className='sectionTitleContainer sectionTitleContainer-cards padded-left'
|
className='sectionTitleContainer sectionTitleContainer-cards padded-left'
|
||||||
dangerouslySetInnerHTML={createLinkElement({
|
dangerouslySetInnerHTML={createLinkElement({
|
||||||
|
|
|
@ -610,7 +610,7 @@ export default function () {
|
||||||
function onTimeUpdate() {
|
function onTimeUpdate() {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
|
|
||||||
if (!(now - lastUpdateTime < 700)) {
|
if (now - lastUpdateTime >= 700) {
|
||||||
lastUpdateTime = now;
|
lastUpdateTime = now;
|
||||||
const player = this;
|
const player = this;
|
||||||
currentRuntimeTicks = playbackManager.duration(player);
|
currentRuntimeTicks = playbackManager.duration(player);
|
||||||
|
|
|
@ -125,7 +125,7 @@ const LiveTVSearchResults: FunctionComponent<LiveTVSearchResultsProps> = ({ serv
|
||||||
'searchResults',
|
'searchResults',
|
||||||
'padded-bottom-page',
|
'padded-bottom-page',
|
||||||
'padded-top',
|
'padded-top',
|
||||||
{ 'hide': !query || !(collectionType === 'livetv') }
|
{ 'hide': !query || collectionType !== 'livetv' }
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<SearchResultsRow
|
<SearchResultsRow
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import debounce from 'lodash-es/debounce';
|
import debounce from 'lodash-es/debounce';
|
||||||
import React, { FunctionComponent, useEffect, useMemo, useRef } from 'react';
|
import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
import AlphaPicker from '../alphaPicker/AlphaPickerComponent';
|
import AlphaPicker from '../alphaPicker/AlphaPickerComponent';
|
||||||
import globalize from '../../scripts/globalize';
|
import globalize from '../../scripts/globalize';
|
||||||
|
@ -53,7 +53,7 @@ const SearchFields: FunctionComponent<SearchFieldsProps> = ({ onSearch = () => {
|
||||||
};
|
};
|
||||||
}, [debouncedOnSearch]);
|
}, [debouncedOnSearch]);
|
||||||
|
|
||||||
const onAlphaPicked = (e: Event) => {
|
const onAlphaPicked = useCallback((e: Event) => {
|
||||||
const value = (e as CustomEvent).detail.value;
|
const value = (e as CustomEvent).detail.value;
|
||||||
const searchInput = getSearchInput();
|
const searchInput = getSearchInput();
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ const SearchFields: FunctionComponent<SearchFieldsProps> = ({ onSearch = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
searchInput.dispatchEvent(new CustomEvent('input', { bubbles: true }));
|
searchInput.dispatchEvent(new CustomEvent('input', { bubbles: true }));
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1106,7 +1106,7 @@ class ItemsView {
|
||||||
const filters = [];
|
const filters = [];
|
||||||
const params = this.params;
|
const params = this.params;
|
||||||
|
|
||||||
if (!(params.type === 'nextup')) {
|
if (params.type !== 'nextup') {
|
||||||
if (params.type === 'Programs') {
|
if (params.type === 'Programs') {
|
||||||
filters.push('Genres');
|
filters.push('Genres');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -585,7 +585,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
||||||
if (isEnabled && currentItem) {
|
if (isEnabled && currentItem) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
|
|
||||||
if (!(now - lastUpdateTime < 700)) {
|
if (now - lastUpdateTime >= 700) {
|
||||||
lastUpdateTime = now;
|
lastUpdateTime = now;
|
||||||
const player = this;
|
const player = this;
|
||||||
currentRuntimeTicks = playbackManager.duration(player);
|
currentRuntimeTicks = playbackManager.duration(player);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { AnchorHTMLAttributes, DetailedHTMLProps, MouseEvent } from 'react';
|
import React, { AnchorHTMLAttributes, DetailedHTMLProps, MouseEvent, useCallback } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import layoutManager from '../../components/layoutManager';
|
import layoutManager from '../../components/layoutManager';
|
||||||
import shell from '../../scripts/shell';
|
import shell from '../../scripts/shell';
|
||||||
|
@ -23,7 +23,7 @@ const LinkButton: React.FC<LinkButtonProps> = ({
|
||||||
children,
|
children,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const onAnchorClick = (e: MouseEvent<HTMLAnchorElement>) => {
|
const onAnchorClick = useCallback((e: MouseEvent<HTMLAnchorElement>) => {
|
||||||
const url = href || '';
|
const url = href || '';
|
||||||
if (url !== '#') {
|
if (url !== '#') {
|
||||||
if (target) {
|
if (target) {
|
||||||
|
@ -38,7 +38,7 @@ const LinkButton: React.FC<LinkButtonProps> = ({
|
||||||
} else {
|
} else {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
}, [ href, target ]);
|
||||||
|
|
||||||
if (isAutoHideEnabled === true && !appHost.supports('externallinks')) {
|
if (isAutoHideEnabled === true && !appHost.supports('externallinks')) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -29,7 +29,7 @@ const ScrollButtons: FC<ScrollButtonsProps> = ({ scrollerFactoryRef, scrollState
|
||||||
}
|
}
|
||||||
}, [scrollerFactoryRef]);
|
}, [scrollerFactoryRef]);
|
||||||
|
|
||||||
const onScrollButtonClick = (direction: Direction) => {
|
const onScrollButtonClick = useCallback((direction: Direction) => {
|
||||||
let newPos;
|
let newPos;
|
||||||
if (direction === Direction.LEFT) {
|
if (direction === Direction.LEFT) {
|
||||||
newPos = Math.max(0, scrollState.scrollPos - scrollState.scrollSize);
|
newPos = Math.max(0, scrollState.scrollPos - scrollState.scrollSize);
|
||||||
|
@ -44,7 +44,10 @@ const ScrollButtons: FC<ScrollButtonsProps> = ({ scrollerFactoryRef, scrollState
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToPosition(newPos, false);
|
scrollToPosition(newPos, false);
|
||||||
};
|
}, [ scrollState.scrollPos, scrollState.scrollSize, scrollToPosition ]);
|
||||||
|
|
||||||
|
const triggerScrollLeft = useCallback(() => onScrollButtonClick(Direction.LEFT), [ onScrollButtonClick ]);
|
||||||
|
const triggerScrollRight = useCallback(() => onScrollButtonClick(Direction.RIGHT), [ onScrollButtonClick ]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parent = scrollButtonsRef.current?.parentNode as HTMLDivElement;
|
const parent = scrollButtonsRef.current?.parentNode as HTMLDivElement;
|
||||||
|
@ -63,7 +66,7 @@ const ScrollButtons: FC<ScrollButtonsProps> = ({ scrollerFactoryRef, scrollState
|
||||||
<IconButton
|
<IconButton
|
||||||
type='button'
|
type='button'
|
||||||
className='emby-scrollbuttons-button btnPrev'
|
className='emby-scrollbuttons-button btnPrev'
|
||||||
onClick={() => onScrollButtonClick(Direction.LEFT)}
|
onClick={triggerScrollLeft}
|
||||||
icon='chevron_left'
|
icon='chevron_left'
|
||||||
disabled={localeScrollPos > 0 ? false : true}
|
disabled={localeScrollPos > 0 ? false : true}
|
||||||
/>
|
/>
|
||||||
|
@ -71,7 +74,7 @@ const ScrollButtons: FC<ScrollButtonsProps> = ({ scrollerFactoryRef, scrollState
|
||||||
<IconButton
|
<IconButton
|
||||||
type='button'
|
type='button'
|
||||||
className='emby-scrollbuttons-button btnNext'
|
className='emby-scrollbuttons-button btnNext'
|
||||||
onClick={() => onScrollButtonClick(Direction.RIGHT)}
|
onClick={triggerScrollRight}
|
||||||
icon='chevron_right'
|
icon='chevron_right'
|
||||||
disabled={scrollState.scrollWidth > 0 && localeScrollPos + scrollState.scrollSize >= scrollState.scrollWidth ? true : false}
|
disabled={scrollState.scrollWidth > 0 && localeScrollPos + scrollState.scrollSize >= scrollState.scrollWidth ? true : false}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { Api } from '@jellyfin/sdk';
|
import type { Api } from '@jellyfin/sdk';
|
||||||
import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
|
import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
|
||||||
import type { ApiClient, Event } from 'jellyfin-apiclient';
|
import type { ApiClient, Event } from 'jellyfin-apiclient';
|
||||||
import React, { createContext, FC, useContext, useEffect, useState } from 'react';
|
import React, { createContext, FC, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import ServerConnections from '../components/ServerConnections';
|
import ServerConnections from '../components/ServerConnections';
|
||||||
import events from '../utils/events';
|
import events from '../utils/events';
|
||||||
|
@ -21,6 +21,12 @@ export const ApiProvider: FC = ({ children }) => {
|
||||||
const [ api, setApi ] = useState<Api>();
|
const [ api, setApi ] = useState<Api>();
|
||||||
const [ user, setUser ] = useState<UserDto>();
|
const [ user, setUser ] = useState<UserDto>();
|
||||||
|
|
||||||
|
const context = useMemo(() => ({
|
||||||
|
__legacyApiClient__: legacyApiClient,
|
||||||
|
api,
|
||||||
|
user
|
||||||
|
}), [ api, legacyApiClient, user ]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ServerConnections.currentApiClient()
|
ServerConnections.currentApiClient()
|
||||||
?.getCurrentUser()
|
?.getCurrentUser()
|
||||||
|
@ -56,11 +62,7 @@ export const ApiProvider: FC = ({ children }) => {
|
||||||
}, [ legacyApiClient, setApi ]);
|
}, [ legacyApiClient, setApi ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApiContext.Provider value={{
|
<ApiContext.Provider value={context}>
|
||||||
__legacyApiClient__: legacyApiClient,
|
|
||||||
api,
|
|
||||||
user
|
|
||||||
}}>
|
|
||||||
{children}
|
{children}
|
||||||
</ApiContext.Provider>
|
</ApiContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -143,8 +143,8 @@ const SuggestionsView: FC<LibraryViewProps> = ({topParentId}) => {
|
||||||
{!recommendations.length ? <div className='noItemsMessage centerMessage'>
|
{!recommendations.length ? <div className='noItemsMessage centerMessage'>
|
||||||
<h1>{globalize.translate('MessageNothingHere')}</h1>
|
<h1>{globalize.translate('MessageNothingHere')}</h1>
|
||||||
<p>{globalize.translate('MessageNoMovieSuggestionsAvailable')}</p>
|
<p>{globalize.translate('MessageNoMovieSuggestionsAvailable')}</p>
|
||||||
</div> : recommendations.map((recommendation, index) => {
|
</div> : recommendations.map(recommendation => {
|
||||||
return <RecommendationContainer key={index} getPortraitShape={getPortraitShape} enableScrollX={enableScrollX} recommendation={recommendation} />;
|
return <RecommendationContainer key={recommendation.CategoryId} getPortraitShape={getPortraitShape} enableScrollX={enableScrollX} recommendation={recommendation} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -379,9 +379,9 @@ const UserParentalControl: FunctionComponent = () => {
|
||||||
isLinkVisible={false}
|
isLinkVisible={false}
|
||||||
/>
|
/>
|
||||||
<div className='blockedTags' style={{marginTop: '.5em'}}>
|
<div className='blockedTags' style={{marginTop: '.5em'}}>
|
||||||
{blockedTags.map((tag, index) => {
|
{blockedTags.map(tag => {
|
||||||
return <BlockedTagList
|
return <BlockedTagList
|
||||||
key={index}
|
key={tag}
|
||||||
tag={tag}
|
tag={tag}
|
||||||
/>;
|
/>;
|
||||||
})}
|
})}
|
||||||
|
@ -401,7 +401,7 @@ const UserParentalControl: FunctionComponent = () => {
|
||||||
<div className='accessScheduleList paperList'>
|
<div className='accessScheduleList paperList'>
|
||||||
{accessSchedules.map((accessSchedule, index) => {
|
{accessSchedules.map((accessSchedule, index) => {
|
||||||
return <AccessScheduleList
|
return <AccessScheduleList
|
||||||
key={index}
|
key={accessSchedule.Id}
|
||||||
index={index}
|
index={index}
|
||||||
Id={accessSchedule.Id}
|
Id={accessSchedule.Id}
|
||||||
DayOfWeek={accessSchedule.DayOfWeek}
|
DayOfWeek={accessSchedule.DayOfWeek}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue