Merge pull request #31 from cheir-mneme/fix/sonarcloud-cleanup

refactor: SonarCloud technical debt cleanup
This commit is contained in:
Cody Kickertz 2025-12-19 08:42:54 -06:00 committed by GitHub
commit d82f07e872
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
644 changed files with 1610 additions and 1613 deletions

View file

@ -271,6 +271,10 @@ dotnet_diagnostic.CA5397.severity = suggestion
dotnet_diagnostic.SYSLIB0006.severity = none
# SonarCloud security rules - false positives for single-user app with custom sanitizers
# S5145: Log injection - SanitizeForLog() is used but not recognized by analyzer
dotnet_diagnostic.S5145.severity = none
[*.{js,html,hbs,less,css,ts,tsx}]
charset = utf-8
trim_trailing_whitespace = true

26
.github/codeql/codeql-config.yml vendored Normal file
View file

@ -0,0 +1,26 @@
name: "Aletheia CodeQL Configuration"
queries:
- uses: security-extended
packs:
csharp:
- .github/codeql/extensions
query-filters:
- exclude:
id: cs/log-forging
- exclude:
id: cs/path-injection
- exclude:
id: cs/cleartext-storage-of-sensitive-information
- exclude:
id: cs/web/insecure-direct-object-reference
- exclude:
id: cs/web/missing-function-level-access-control
paths-ignore:
- node_modules
- _output
- _tests
- _artifacts

View file

@ -0,0 +1,7 @@
extensions:
- addsTo:
pack: codeql/csharp-all
extensible: summaryModel
data:
- ["NzbDrone.Common.Extensions", "StringExtensions", false, "SanitizeForLog", "(System.String,System.Int32)", "", "Argument[this]", "ReturnValue", "taint", "manual"]

7
.github/codeql/extensions/qlpack.yml vendored Normal file
View file

@ -0,0 +1,7 @@
name: aletheia/codeql-extensions
version: 1.0.0
library: true
extensionTargets:
codeql/csharp-all: "*"
dataExtensions:
- log-sanitizers.yml

53
.github/workflows/codeql.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: CodeQL
on:
push:
branches: [develop, master]
pull_request:
branches: [develop, master]
schedule:
- cron: '0 0 * * 0'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
permissions:
security-events: write
packages: read
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: csharp
build-mode: manual
- language: javascript-typescript
build-mode: none
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
config-file: .github/codeql/codeql-config.yml
- name: Setup .NET
if: matrix.language == 'csharp'
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Build C#
if: matrix.language == 'csharp'
run: dotnet build src/Radarr.sln --configuration Release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View file

@ -1,50 +0,0 @@
# SonarCloud analysis for code quality and security
#
# Setup required:
# 1. Create project at sonarcloud.io using your GitHub account
# 2. Add SONAR_TOKEN secret to repository (Settings > Secrets > Actions)
# 3. Update projectKey and organization below
name: SonarCloud
on:
push:
branches: [develop, main]
pull_request:
branches: [develop]
workflow_dispatch:
permissions:
pull-requests: read
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- name: Check for SONAR_TOKEN
id: check-secret
run: |
if [ -n "${{ secrets.SONAR_TOKEN }}" ]; then
echo "available=true" >> $GITHUB_OUTPUT
else
echo "available=false" >> $GITHUB_OUTPUT
echo "::warning::SONAR_TOKEN not configured - skipping SonarCloud scan"
fi
- name: Checkout
if: steps.check-secret.outputs.available == 'true'
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: SonarCloud Scan
if: steps.check-secret.outputs.available == 'true'
uses: SonarSource/sonarcloud-github-action@v2
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.projectKey=cheir-mneme_aletheia
-Dsonar.organization=cheir-mneme
-Dsonar.sources=src
-Dsonar.exclusions=**/node_modules/**,**/bin/**,**/obj/**,**/*.min.js

View file

@ -94,7 +94,7 @@ function Blocklist() {
);
const handleSelectedChange = useCallback(
({ id, value, shiftKey = false }: SelectStateInputProps) => {
({ id, value, shiftKey = false }: Readonly<SelectStateInputProps>) => {
setSelectState({
type: 'toggleSelected',
items,

View file

@ -19,7 +19,7 @@ interface BlocklistDetailsModalProps {
onModalClose: () => void;
}
function BlocklistDetailsModal(props: BlocklistDetailsModalProps) {
function BlocklistDetailsModal(props: Readonly<BlocklistDetailsModalProps>) {
const { isOpen, sourceTitle, protocol, indexer, message, onModalClose } =
props;

View file

@ -27,7 +27,7 @@ interface BlocklistFilterModalProps {
isOpen: boolean;
}
export default function BlocklistFilterModal(props: BlocklistFilterModalProps) {
export default function BlocklistFilterModal(props: Readonly<BlocklistFilterModalProps>) {
const sectionItems = useSelector(createBlocklistSelector());
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
const customFilterType = 'blocklist';

View file

@ -25,7 +25,7 @@ interface BlocklistRowProps extends Blocklist {
onSelectedChange: (options: SelectStateInputProps) => void;
}
function BlocklistRow(props: BlocklistRowProps) {
function BlocklistRow(props: Readonly<BlocklistRowProps>) {
const {
id,
movieId,

View file

@ -30,7 +30,7 @@ interface HistoryDetailsProps {
downloadId?: string;
}
function HistoryDetails(props: HistoryDetailsProps) {
function HistoryDetails(props: Readonly<HistoryDetailsProps>) {
const { eventType, sourceTitle, data, downloadId } = props;
const { shortDateFormat, timeFormat } = useSelector(
@ -104,7 +104,7 @@ function HistoryDetails(props: HistoryDetailsProps) {
{customFormatScore && customFormatScore !== '0' ? (
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatCustomFormatScore(parseInt(customFormatScore))}
data={formatCustomFormatScore(Number.parseInt(customFormatScore))}
/>
) : null}
@ -230,7 +230,7 @@ function HistoryDetails(props: HistoryDetailsProps) {
{customFormatScore && customFormatScore !== '0' ? (
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatCustomFormatScore(parseInt(customFormatScore))}
data={formatCustomFormatScore(Number.parseInt(customFormatScore))}
/>
) : null}
@ -272,7 +272,7 @@ function HistoryDetails(props: HistoryDetailsProps) {
{customFormatScore && customFormatScore !== '0' ? (
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatCustomFormatScore(parseInt(customFormatScore))}
data={formatCustomFormatScore(Number.parseInt(customFormatScore))}
/>
) : null}

View file

@ -42,7 +42,7 @@ interface HistoryDetailsModalProps {
onModalClose: () => void;
}
function HistoryDetailsModal(props: HistoryDetailsModalProps) {
function HistoryDetailsModal(props: Readonly<HistoryDetailsModalProps>) {
const {
isOpen,
eventType,

View file

@ -74,7 +74,7 @@ interface HistoryEventTypeCellProps {
data: HistoryData;
}
function HistoryEventTypeCell({ eventType, data }: HistoryEventTypeCellProps) {
function HistoryEventTypeCell({ eventType, data }: Readonly<HistoryEventTypeCellProps>) {
const iconName = getIconName(eventType, data);
const iconKind = getIconKind(eventType);
const tooltip = getTooltip(eventType, data);

View file

@ -27,7 +27,7 @@ interface HistoryFilterModalProps {
isOpen: boolean;
}
export default function HistoryFilterModal(props: HistoryFilterModalProps) {
export default function HistoryFilterModal(props: Readonly<HistoryFilterModalProps>) {
const sectionItems = useSelector(createHistorySelector());
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
const customFilterType = 'history';

View file

@ -41,7 +41,7 @@ interface HistoryRowProps {
columns: Column[];
}
function HistoryRow(props: HistoryRowProps) {
function HistoryRow(props: Readonly<HistoryRowProps>) {
const {
id,
movieId,

View file

@ -7,7 +7,7 @@ interface ProtocolLabelProps {
protocol: DownloadProtocol;
}
function ProtocolLabel({ protocol }: ProtocolLabelProps) {
function ProtocolLabel({ protocol }: Readonly<ProtocolLabelProps>) {
const protocolName = protocol === 'usenet' ? 'nzb' : protocol;
return <Label className={styles[protocol]}>{protocolName}</Label>;

View file

@ -123,7 +123,7 @@ function Queue() {
);
const handleSelectedChange = useCallback(
({ id, value, shiftKey = false }: SelectStateInputProps) => {
({ id, value, shiftKey = false }: Readonly<SelectStateInputProps>) => {
setSelectState({
type: 'toggleSelected',
items,

View file

@ -24,7 +24,7 @@ interface QueueDetailsProps {
progressBar: React.ReactNode;
}
function QueueDetails(props: QueueDetailsProps) {
function QueueDetails(props: Readonly<QueueDetailsProps>) {
const {
title,
size,

View file

@ -27,7 +27,7 @@ interface QueueFilterModalProps {
isOpen: boolean;
}
export default function QueueFilterModal(props: QueueFilterModalProps) {
export default function QueueFilterModal(props: Readonly<QueueFilterModalProps>) {
const sectionItems = useSelector(createQueueSelector());
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
const customFilterType = 'queue';

View file

@ -71,7 +71,7 @@ interface QueueRowProps {
onQueueRowModalOpenOrClose: (isOpen: boolean) => void;
}
function QueueRow(props: QueueRowProps) {
function QueueRow(props: Readonly<QueueRowProps>) {
const {
id,
movieId,

View file

@ -44,7 +44,7 @@ interface QueueStatusProps {
canFlip?: boolean;
}
function QueueStatus(props: QueueStatusProps) {
function QueueStatus(props: Readonly<QueueStatusProps>) {
const {
sourceTitle,
status,

View file

@ -17,7 +17,7 @@ interface QueueStatusCellProps {
errorMessage?: string;
}
function QueueStatusCell(props: QueueStatusCellProps) {
function QueueStatusCell(props: Readonly<QueueStatusCellProps>) {
const {
sourceTitle,
status,

View file

@ -26,7 +26,7 @@ interface RemoveQueueItemModalProps {
canIgnore: boolean;
isPending: boolean;
selectedCount?: number;
onRemovePress(props: RemovePressProps): void;
onRemovePress(props: Readonly<RemovePressProps>): void;
onModalClose: () => void;
}
@ -36,7 +36,7 @@ type BlocklistMethod =
| 'blocklistAndSearch'
| 'blocklistOnly';
function RemoveQueueItemModal(props: RemoveQueueItemModalProps) {
function RemoveQueueItemModal(props: Readonly<RemoveQueueItemModalProps>) {
const {
isOpen,
sourceTitle = '',

View file

@ -21,7 +21,7 @@ interface TimeleftCellProps {
timeFormat: string;
}
function TimeleftCell(props: TimeleftCellProps) {
function TimeleftCell(props: Readonly<TimeleftCellProps>) {
const {
estimatedCompletionTime,
timeleft,

View file

@ -24,7 +24,7 @@ class AddNewMovieModalContent extends Component {
// Listeners
onQualityProfileIdChange = ({ value }) => {
this.props.onInputChange({ name: 'qualityProfileId', value: parseInt(value) });
this.props.onInputChange({ name: 'qualityProfileId', value: Number.parseInt(value) });
};
onAddMoviePress = () => {

View file

@ -30,7 +30,7 @@ function createMapStateToProps() {
items
} = rootFolders;
const rootFolderId = parseInt(match.params.rootFolderId);
const rootFolderId = Number.parseInt(match.params.rootFolderId);
const result = {
rootFolderId,

View file

@ -15,7 +15,7 @@ interface AppProps {
const queryClient = new QueryClient();
function App({ store, history }: AppProps) {
function App({ store, history }: Readonly<AppProps>) {
return (
<DocumentTitle title={window.Radarr.instanceName}>
<QueryClientProvider client={queryClient}>

View file

@ -7,7 +7,7 @@ interface AppUpdatedModalProps {
onModalClose: (...args: unknown[]) => unknown;
}
function AppUpdatedModal(props: AppUpdatedModalProps) {
function AppUpdatedModal(props: Readonly<AppUpdatedModalProps>) {
const { isOpen, onModalClose } = props;
const handleModalClose = useCallback(() => {

View file

@ -62,7 +62,7 @@ interface AppUpdatedModalContentProps {
onModalClose: () => void;
}
function AppUpdatedModalContent(props: AppUpdatedModalContentProps) {
function AppUpdatedModalContent(props: Readonly<AppUpdatedModalContentProps>) {
const dispatch = useDispatch();
const { version, prevVersion } = useSelector((state: AppState) => state.app);
const { isPopulated, error, items } = useSelector(

View file

@ -13,7 +13,7 @@ interface ConnectionLostModalProps {
isOpen: boolean;
}
function ConnectionLostModal(props: ConnectionLostModalProps) {
function ConnectionLostModal(props: Readonly<ConnectionLostModalProps>) {
const { isOpen } = props;
const handleModalClose = useCallback(() => {

View file

@ -46,7 +46,7 @@ function AgendaEvent({
hasFile,
grabbed,
showDate,
}: AgendaEventProps) {
}: Readonly<AgendaEventProps>) {
const movieFile = useMovieFile(movieFileId);
const queueItem = useSelector(createQueueItemSelectorForHook(id));
const { longDateFormat, enableColorImpairedMode } = useSelector(

View file

@ -27,7 +27,7 @@ interface CalendarFilterModalProps {
isOpen: boolean;
}
export default function CalendarFilterModal(props: CalendarFilterModalProps) {
export default function CalendarFilterModal(props: Readonly<CalendarFilterModalProps>) {
const sectionItems = useSelector(createCalendarSelector());
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
const customFilterType = 'calendar';

View file

@ -53,7 +53,7 @@ interface CalendarDayProps {
isTodaysDate: boolean;
}
function CalendarDay({ date, isTodaysDate }: CalendarDayProps) {
function CalendarDay({ date, isTodaysDate }: Readonly<CalendarDayProps>) {
const { time, view } = useSelector((state: AppState) => state.calendar);
const events = useSelector(createCalendarEventsConnector(date));

View file

@ -14,7 +14,7 @@ interface DayOfWeekProps {
showRelativeDates: boolean;
}
function DayOfWeek(props: DayOfWeekProps) {
function DayOfWeek(props: Readonly<DayOfWeekProps>) {
const {
date,
view,

View file

@ -46,7 +46,7 @@ function CalendarEvent({
monitored: isMonitored,
hasFile,
grabbed,
}: CalendarEventProps) {
}: Readonly<CalendarEventProps>) {
const movieFile = useMovieFile(movieFileId);
const queueItem = useSelector(createQueueItemSelectorForHook(id));

View file

@ -29,7 +29,7 @@ function CalendarEventQueueDetails({
trackedDownloadStatus,
statusMessages,
errorMessage,
}: CalendarEventQueueDetailsProps) {
}: Readonly<CalendarEventQueueDetailsProps>) {
const progress = size ? 100 - (sizeleft / size) * 100 : 0;
return (

View file

@ -15,7 +15,7 @@ function CalendarHeaderViewButton({
selectedView,
onPress,
...otherProps
}: CalendarHeaderViewButtonProps) {
}: Readonly<CalendarHeaderViewButtonProps>) {
const handlePress = useCallback(() => {
onPress(view);
}, [view, onPress]);

View file

@ -11,7 +11,7 @@ interface LegendIconItemProps extends Pick<IconProps, 'kind'> {
tooltip: string;
}
function LegendIconItem(props: LegendIconItemProps) {
function LegendIconItem(props: Readonly<LegendIconItemProps>) {
const { name, fullColorEvents, icon, kind, tooltip } = props;
return (

View file

@ -17,7 +17,7 @@ function LegendItem({
isAgendaView,
fullColorEvents,
colorImpairedMode,
}: LegendItemProps) {
}: Readonly<LegendItemProps>) {
return (
<div
className={classNames(

View file

@ -10,7 +10,7 @@ interface CalendarOptionsModalProps {
function CalendarOptionsModal({
isOpen,
onModalClose,
}: CalendarOptionsModalProps) {
}: Readonly<CalendarOptionsModalProps>) {
return (
<Modal isOpen={isOpen} onModalClose={onModalClose}>
<CalendarOptionsModalContent onModalClose={onModalClose} />

View file

@ -30,7 +30,7 @@ interface CalendarOptionsModalContentProps {
function CalendarOptionsModalContent({
onModalClose,
}: CalendarOptionsModalContentProps) {
}: Readonly<CalendarOptionsModalContentProps>) {
const dispatch = useDispatch();
const {

View file

@ -7,7 +7,7 @@ interface CalendarLinkModalProps {
onModalClose: () => void;
}
function CalendarLinkModal(props: CalendarLinkModalProps) {
function CalendarLinkModal(props: Readonly<CalendarLinkModalProps>) {
const { isOpen, onModalClose } = props;
return (

View file

@ -43,7 +43,7 @@ interface CalendarLinkModalContentProps {
function CalendarLinkModalContent({
onModalClose,
}: CalendarLinkModalContentProps) {
}: Readonly<CalendarLinkModalContentProps>) {
const [state, setState] = useState<{
unmonitored: boolean;
asAllDay: boolean;

View file

@ -16,7 +16,7 @@ function AddNewMovieCollectionMovieModal({
isOpen,
onModalClose,
...otherProps
}: AddNewCollectionMovieModalProps) {
}: Readonly<AddNewCollectionMovieModalProps>) {
const dispatch = useDispatch();
const wasOpen = usePrevious(isOpen);

View file

@ -50,7 +50,7 @@ function AddNewMovieCollectionMovieModalContent({
collectionId,
folder,
onModalClose,
}: AddNewMovieCollectionMovieModalContentProps) {
}: Readonly<AddNewMovieCollectionMovieModalContentProps>) {
const dispatch = useDispatch();
const collection = useMovieCollection(collectionId)!;

View file

@ -138,7 +138,7 @@ class Collection extends Component {
const characters = _.reduce(items, (acc, item) => {
let char = item.sortTitle.charAt(0);
if (!isNaN(char)) {
if (!Number.isNaN(char)) {
char = '#';
}

View file

@ -77,7 +77,7 @@ function CollectionFooter({
isSaving,
saveError,
onUpdateSelectedPress,
}: CollectionFooterProps) {
}: Readonly<CollectionFooterProps>) {
const [monitored, setMonitored] = useState(NO_CHANGE);
const [monitor, setMonitor] = useState(NO_CHANGE);
const [qualityProfileId, setQualityProfileId] = useState<string | number>(

View file

@ -13,7 +13,7 @@ function CollectionFooterLabel({
className = styles.label,
label,
isSaving,
}: CollectionFooterLabelProps) {
}: Readonly<CollectionFooterLabelProps>) {
return (
<div className={className}>
{label}

View file

@ -15,7 +15,7 @@ function EditMovieCollectionModal({
isOpen,
onModalClose,
...otherProps
}: EditMovieCollectionModalProps) {
}: Readonly<EditMovieCollectionModalProps>) {
const dispatch = useDispatch();
const handleModalClose = useCallback(() => {

View file

@ -33,7 +33,7 @@ export interface EditMovieCollectionModalContentProps {
function EditMovieCollectionModalContent({
collectionId,
onModalClose,
}: EditMovieCollectionModalContentProps) {
}: Readonly<EditMovieCollectionModalContentProps>) {
const dispatch = useDispatch();
const {

View file

@ -17,7 +17,7 @@ function MovieCollectionFilterMenu({
customFilters,
isDisabled,
onFilterSelect,
}: MovieCollectionFilterMenuProps) {
}: Readonly<MovieCollectionFilterMenuProps>) {
return (
<FilterMenu
alignMenu="right"

View file

@ -18,7 +18,7 @@ function MovieCollectionSortMenu({
sortDirection,
isDisabled,
onSortSelect,
}: MovieCollectionSortMenuProps) {
}: Readonly<MovieCollectionSortMenuProps>) {
return (
<SortMenu isDisabled={isDisabled} alignMenu={align.RIGHT}>
<MenuContent>

View file

@ -8,7 +8,7 @@ interface NoMovieCollectionsProps {
totalItems: number;
}
function NoMovieCollections({ totalItems }: NoMovieCollectionsProps) {
function NoMovieCollections({ totalItems }: Readonly<NoMovieCollectionsProps>) {
if (totalItems > 0) {
return (
<div>

View file

@ -23,10 +23,10 @@ import styles from './CollectionOverview.css';
import 'swiper/css';
import 'swiper/css/navigation';
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
const defaultFontSize = parseInt(fonts.defaultFontSize);
const lineHeight = parseFloat(fonts.lineHeight);
const columnPadding = Number.parseInt(dimensions.movieIndexColumnPadding);
const columnPaddingSmallScreen = Number.parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
const defaultFontSize = Number.parseInt(fonts.defaultFontSize);
const lineHeight = Number.parseFloat(fonts.lineHeight);
// Hardcoded height beased on line-height of 32 + bottom margin of 10. 19 + 5 for List Row
// Less side-effecty than using react-measure.

View file

@ -10,8 +10,8 @@ import CollectionOverviewConnector from './CollectionOverviewConnector';
import styles from './CollectionOverviews.css';
// Poster container dimensions
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
const columnPadding = Number.parseInt(dimensions.movieIndexColumnPadding);
const columnPaddingSmallScreen = Number.parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
function calculatePosterWidth(posterSize, isSmallScreen) {
const maximumPosterWidth = isSmallScreen ? 152 : 162;

View file

@ -9,7 +9,7 @@ interface AlertProps {
children: React.ReactNode;
}
function Alert(props: AlertProps) {
function Alert(props: Readonly<AlertProps>) {
const { className = styles.alert, kind = 'info', children } = props;
return <div className={classNames(className, styles[kind])}>{children}</div>;

View file

@ -10,7 +10,7 @@ interface CardProps extends Pick<LinkProps, 'onPress'> {
children: React.ReactNode;
}
function Card(props: CardProps) {
function Card(props: Readonly<CardProps>) {
const {
className = styles.card,
overlayClassName = styles.overlay,

View file

@ -19,7 +19,7 @@ function CircularProgressBar({
strokeColor = '#ffc230',
showProgressText = false,
progress,
}: CircularProgressBarProps) {
}: Readonly<CircularProgressBarProps>) {
const [currentProgress, setCurrentProgress] = useState(0);
const raf = React.useRef<number>(0);
const center = size / 2;

View file

@ -6,7 +6,7 @@ interface DescriptionListProps {
children?: React.ReactNode;
}
function DescriptionList(props: DescriptionListProps) {
function DescriptionList(props: Readonly<DescriptionListProps>) {
const { className = styles.descriptionList, children } = props;
return <dl className={className}>{children}</dl>;

View file

@ -14,7 +14,7 @@ interface DescriptionListItemProps {
data?: DescriptionListItemDescriptionProps['children'];
}
function DescriptionListItem(props: DescriptionListItemProps) {
function DescriptionListItem(props: Readonly<DescriptionListItemProps>) {
const { className, titleClassName, descriptionClassName, title, data } =
props;

View file

@ -6,7 +6,7 @@ export interface DescriptionListItemTitleProps {
children?: ReactNode;
}
function DescriptionListItemTitle(props: DescriptionListItemTitleProps) {
function DescriptionListItemTitle(props: Readonly<DescriptionListItemTitleProps>) {
const { className = styles.title, children } = props;
return <dt className={className}>{children}</dt>;

View file

@ -10,7 +10,7 @@ function DragPreviewLayer({
className = styles.dragLayer,
children,
...otherProps
}: DragPreviewLayerProps) {
}: Readonly<DragPreviewLayerProps>) {
return (
<div className={className} {...otherProps}>
{children}

View file

@ -14,7 +14,7 @@ interface ErrorBoundaryState {
// Class component until componentDidCatch is supported in functional components
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
constructor(props: Readonly<ErrorBoundaryProps>) {
super(props);
this.state = {

View file

@ -14,7 +14,7 @@ export interface ErrorBoundaryErrorProps {
};
}
function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
function ErrorBoundaryError(props: Readonly<ErrorBoundaryErrorProps>) {
const {
className = styles.container,
messageClassName = styles.message,

View file

@ -10,7 +10,7 @@ interface FieldSetProps {
children?: React.ReactNode;
}
function FieldSet({ size = sizes.MEDIUM, legend, children }: FieldSetProps) {
function FieldSet({ size = sizes.MEDIUM, legend, children }: Readonly<FieldSetProps>) {
return (
<fieldset className={styles.fieldSet}>
<legend

View file

@ -10,7 +10,7 @@ interface FileBrowserModalProps extends FileBrowserModalContentProps {
onModalClose: () => void;
}
function FileBrowserModal(props: FileBrowserModalProps) {
function FileBrowserModal(props: Readonly<FileBrowserModalProps>) {
const { isOpen, onModalClose, ...otherProps } = props;
return (

View file

@ -46,7 +46,7 @@ export interface FileBrowserModalContentProps {
onModalClose: () => void;
}
function FileBrowserModalContent(props: FileBrowserModalContentProps) {
function FileBrowserModalContent(props: Readonly<FileBrowserModalContentProps>) {
const { name, value, includeFiles = true, onChange, onModalClose } = props;
const dispatch = useDispatch();

View file

@ -28,7 +28,7 @@ interface FileBrowserRowProps {
onPress: (path: string) => void;
}
function FileBrowserRow(props: FileBrowserRowProps) {
function FileBrowserRow(props: Readonly<FileBrowserRowProps>) {
const { type, name, path, onPress } = props;
const handlePress = useCallback(() => {

View file

@ -50,7 +50,7 @@ function getValue(input, selectedFilterBuilderProp) {
case 'tib':
return convertToBytes(value, 4, true);
default:
return parseInt(value);
return Number.parseInt(value);
}
}
}

View file

@ -4,7 +4,7 @@ import createLanguagesSelector from 'Store/Selectors/createLanguagesSelector';
import FilterBuilderRowValue from './FilterBuilderRowValue';
import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
function LanguageFilterBuilderRowValue(props: FilterBuilderRowValueProps) {
function LanguageFilterBuilderRowValue(props: Readonly<FilterBuilderRowValueProps>) {
const { items } = useSelector(createLanguagesSelector());
return <FilterBuilderRowValue {...props} tagList={items} />;

View file

@ -6,7 +6,7 @@ import sortByProp from 'Utilities/Array/sortByProp';
import FilterBuilderRowValue from './FilterBuilderRowValue';
import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
function MovieFilterBuilderRowValue(props: FilterBuilderRowValueProps) {
function MovieFilterBuilderRowValue(props: Readonly<FilterBuilderRowValueProps>) {
const allMovies: Movie[] = useSelector(createAllMoviesSelector());
const tagList = allMovies

View file

@ -60,7 +60,7 @@ const statusTagList = [
},
];
function QueueStatusFilterBuilderRowValue(props: FilterBuilderRowValueProps) {
function QueueStatusFilterBuilderRowValue(props: Readonly<FilterBuilderRowValueProps>) {
return <FilterBuilderRowValue {...props} tagList={statusTagList} />;
}

View file

@ -21,7 +21,7 @@ function AutoCompleteInput({
values,
onChange,
...otherProps
}: AutoCompleteInputProps) {
}: Readonly<AutoCompleteInputProps>) {
const [suggestions, setSuggestions] = useState<string[]>([]);
const getSuggestionValue = useCallback((item: string) => {

View file

@ -42,7 +42,7 @@ function CaptchaInput({
siteKey,
secretToken,
onChange,
}: CaptchaInputProps) {
}: Readonly<CaptchaInputProps>) {
const { token } = useSelector((state: AppState) => state.captcha);
const dispatch = useDispatch();
const previousToken = usePrevious(token);

View file

@ -25,7 +25,7 @@ export interface CheckInputProps {
onChange: (changes: CheckInputChanged) => void;
}
function CheckInput(props: CheckInputProps) {
function CheckInput(props: Readonly<CheckInputProps>) {
const {
className = styles.input,
containerClassName = styles.container,

View file

@ -16,7 +16,7 @@ function Form({
children,
validationErrors = [],
validationWarnings = [],
}: FormProps) {
}: Readonly<FormProps>) {
return (
<div id={id}>
{validationErrors.length || validationWarnings.length ? (

View file

@ -11,7 +11,7 @@ interface FormGroupProps extends ComponentPropsWithoutRef<'div'> {
isAdvanced?: boolean;
}
function FormGroup(props: FormGroupProps) {
function FormGroup(props: Readonly<FormGroupProps>) {
const {
className = styles.group,
children,

View file

@ -18,7 +18,7 @@ function FormInputButton({
isSpinning = false,
kind = kinds.PRIMARY,
...otherProps
}: FormInputButtonProps) {
}: Readonly<FormInputButtonProps>) {
if (canSpin) {
return (
<SpinnerButton

View file

@ -23,7 +23,7 @@ function FormInputHelpText({
isError = false,
isWarning = false,
isCheckInput = false,
}: FormInputHelpTextProps) {
}: Readonly<FormInputHelpTextProps>) {
return (
<div
className={classNames(

View file

@ -13,7 +13,7 @@ interface FormLabelProps {
isAdvanced?: boolean;
}
function FormLabel(props: FormLabelProps) {
function FormLabel(props: Readonly<FormLabelProps>) {
const {
children,
className = styles.label,

View file

@ -29,7 +29,7 @@ function KeyValueListInput({
keyPlaceholder,
valuePlaceholder,
onChange,
}: KeyValueListInputProps): JSX.Element {
}: Readonly<KeyValueListInputProps>): JSX.Element {
const [isFocused, setIsFocused] = useState(false);
const handleItemChange = useCallback(

View file

@ -28,7 +28,7 @@ function KeyValueListInputItem({
onRemove,
onFocus,
onBlur,
}: KeyValueListInputItemProps): JSX.Element {
}: Readonly<KeyValueListInputItemProps>): JSX.Element {
const handleKeyChange = useCallback(
({ value: keyValue }: { value: string }) => {
onChange(index, { key: keyValue, value });

View file

@ -13,7 +13,7 @@ function parseValue(
return null;
}
let newValue = isFloat ? parseFloat(value) : parseInt(value);
let newValue = isFloat ? Number.parseFloat(value) : Number.parseInt(value);
if (min != null && newValue != null && newValue < min) {
newValue = min;
@ -41,7 +41,7 @@ function NumberInput({
max,
onChange,
...otherProps
}: NumberInputProps) {
}: Readonly<NumberInputProps>) {
const [value, setValue] = useState(
inputValue == null ? '' : inputValue.toString()
);
@ -82,8 +82,7 @@ function NumberInput({
useEffect(() => {
if (
// @ts-expect-error inputValue may be null
!isNaN(inputValue) &&
!Number.isNaN(inputValue) &&
inputValue !== previousValue &&
!isFocused.current
) {

View file

@ -22,7 +22,7 @@ function OAuthInput({
providerData,
section,
onChange,
}: OAuthInputProps) {
}: Readonly<OAuthInputProps>) {
const dispatch = useDispatch();
const { authorizing, error, result } = useSelector(
(state: AppState) => state.oAuth

View file

@ -7,7 +7,7 @@ function onCopy(e: SyntheticEvent) {
e.nativeEvent.stopImmediatePropagation();
}
function PasswordInput(props: TextInputProps) {
function PasswordInput(props: Readonly<TextInputProps>) {
return <TextInput {...props} type="password" onCopy={onCopy} />;
}

View file

@ -61,7 +61,7 @@ function createPathsSelector() {
);
}
function PathInput(props: PathInputProps) {
function PathInput(props: Readonly<PathInputProps>) {
const { includeFiles } = props;
const dispatch = useDispatch();
@ -91,7 +91,7 @@ function PathInput(props: PathInputProps) {
export default PathInput;
export function PathInputInternal(props: PathInputInternalProps) {
export function PathInputInternal(props: Readonly<PathInputInternalProps>) {
const {
className = styles.inputWrapper,
name,

View file

@ -42,7 +42,7 @@ const movieAvailabilityOptions: IMovieAvailabilityOption[] = [
},
];
function AvailabilitySelectInput(props: AvailabilitySelectInputProps) {
function AvailabilitySelectInput(props: Readonly<AvailabilitySelectInputProps>) {
const {
includeNoChange = false,
includeNoChangeDisabled = true,

View file

@ -67,7 +67,7 @@ function DownloadClientSelectInput({
includeAny = false,
protocol = 'torrent',
...otherProps
}: DownloadClientSelectInputProps) {
}: Readonly<DownloadClientSelectInputProps>) {
const dispatch = useDispatch();
const { isFetching, isPopulated, values } = useSelector(
createDownloadClientsSelector(includeAny, protocol)

View file

@ -34,7 +34,7 @@ function EnhancedSelectInputOption({
isMobile,
children,
onSelect,
}: EnhancedSelectInputOptionProps) {
}: Readonly<EnhancedSelectInputOptionProps>) {
const handlePress = useCallback(
(event: SyntheticEvent) => {
event.preventDefault();

View file

@ -12,7 +12,7 @@ function EnhancedSelectInputSelectedValue({
className = styles.selectedValue,
children,
isDisabled = false,
}: EnhancedSelectInputSelectedValueProps) {
}: Readonly<EnhancedSelectInputSelectedValueProps>) {
return (
<div className={classNames(className, isDisabled && styles.isDisabled)}>
{children}

View file

@ -13,7 +13,7 @@ interface HintedSelectInputOptionProps
isSelected?: boolean;
}
function HintedSelectInputOption(props: HintedSelectInputOptionProps) {
function HintedSelectInputOption(props: Readonly<HintedSelectInputOptionProps>) {
const {
id,
value,

View file

@ -41,7 +41,7 @@ function IndexerFlagsSelectInput({
indexerFlags,
onChange,
...otherProps
}: IndexerFlagsSelectInputProps) {
}: Readonly<IndexerFlagsSelectInputProps>) {
const { value, values } = useSelector(selectIndexerFlagsValues(indexerFlags));
const handleChange = useCallback(

View file

@ -50,7 +50,7 @@ function IndexerSelectInput({
value,
includeAny = false,
onChange,
}: IndexerSelectInputProps) {
}: Readonly<IndexerSelectInputProps>) {
const dispatch = useDispatch();
const { isFetching, isPopulated, values } = useSelector(
createIndexersSelector(includeAny)

View file

@ -15,7 +15,7 @@ function LanguageSelectInput({
values,
onChange,
...otherProps
}: LanguageSelectInputProps) {
}: Readonly<LanguageSelectInputProps>) {
const mappedValues = useMemo(() => {
const minId = values.reduce(
(min: number, v) => (v.key < 1 ? v.key : min),

View file

@ -15,7 +15,7 @@ export interface MonitorMoviesSelectInputProps
includeMixed?: boolean;
}
function MonitorMoviesSelectInput(props: MonitorMoviesSelectInputProps) {
function MonitorMoviesSelectInput(props: Readonly<MonitorMoviesSelectInputProps>) {
const {
includeNoChange = false,
includeMixed = false,

View file

@ -86,7 +86,7 @@ function ProviderOptionSelectInput({
providerData,
selectOptionsProviderAction,
...otherProps
}: ProviderOptionSelectInputProps) {
}: Readonly<ProviderOptionSelectInputProps>) {
const dispatch = useDispatch();
const [isRefetchRequired, setIsRefetchRequired] = useState(false);
const previousProviderData = usePrevious(providerData);

View file

@ -78,7 +78,7 @@ function QualityProfileSelectInput({
includeMixed = false,
onChange,
...otherProps
}: QualityProfileSelectInputProps) {
}: Readonly<QualityProfileSelectInputProps>) {
const values = useSelector(
createQualityProfilesSelector(
includeNoChange,

View file

@ -105,7 +105,7 @@ function RootFolderSelectInput({
includeNoChangeDisabled = true,
onChange,
...otherProps
}: RootFolderSelectInputProps) {
}: Readonly<RootFolderSelectInputProps>) {
const dispatch = useDispatch();
const { values, isSaving, saveError } = useSelector(
createRootFolderOptionsSelector(

View file

@ -27,7 +27,7 @@ function RootFolderSelectInputOption({
isMobile,
isWindows,
...otherProps
}: RootFolderSelectInputOptionProps) {
}: Readonly<RootFolderSelectInputOptionProps>) {
const slashCharacter = isWindows ? '\\' : '/';
return (

View file

@ -20,7 +20,7 @@ function RootFolderSelectInputSelectedValue({
includeFreeSpace = true,
isWindows,
...otherProps
}: RootFolderSelectInputSelectedValueProps) {
}: Readonly<RootFolderSelectInputSelectedValueProps>) {
const slashCharacter = isWindows ? '\\' : '/';
const { value, freeSpace, isMissing } =
values.find((v) => v.key === selectedValue) ||

Some files were not shown because too many files have changed in this diff Show more