diff --git a/.eslintrc.js b/.eslintrc.js
index 3928729cb4..0e35c8c6b8 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -73,6 +73,7 @@ module.exports = {
'padded-blocks': ['error', 'never'],
'prefer-const': ['error', { 'destructuring': 'all' }],
'@typescript-eslint/prefer-for-of': ['error'],
+ '@typescript-eslint/prefer-optional-chain': ['error'],
'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
'radix': ['error'],
'@typescript-eslint/semi': ['error'],
diff --git a/src/components/backdrop/backdrop.js b/src/components/backdrop/backdrop.js
index 4cd481827f..46655777ff 100644
--- a/src/components/backdrop/backdrop.js
+++ b/src/components/backdrop/backdrop.js
@@ -37,7 +37,7 @@ class Backdrop {
parent.appendChild(backdropImage);
if (!enableAnimation()) {
- if (existingBackdropImage && existingBackdropImage.parentNode) {
+ if (existingBackdropImage?.parentNode) {
existingBackdropImage.parentNode.removeChild(existingBackdropImage);
}
internalBackdrop(true);
@@ -51,7 +51,7 @@ class Backdrop {
if (backdropImage === self.currentAnimatingElement) {
self.currentAnimatingElement = null;
}
- if (existingBackdropImage && existingBackdropImage.parentNode) {
+ if (existingBackdropImage?.parentNode) {
existingBackdropImage.parentNode.removeChild(existingBackdropImage);
}
};
diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js
index 5ad759002d..d5b19c0210 100644
--- a/src/components/cardbuilder/cardBuilder.js
+++ b/src/components/cardbuilder/cardBuilder.js
@@ -546,7 +546,7 @@ function getCardImageUrl(item, apiClient, options, shape) {
imgType = 'Backdrop';
imgTag = item.ParentBackdropImageTags[0];
itemId = item.ParentBackdropItemId;
- } else if (item.ImageTags && item.ImageTags.Primary && (item.Type !== 'Episode' || item.ChildCount !== 0)) {
+ } else if (item.ImageTags?.Primary && (item.Type !== 'Episode' || item.ChildCount !== 0)) {
imgType = 'Primary';
imgTag = item.ImageTags.Primary;
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
@@ -591,10 +591,10 @@ function getCardImageUrl(item, apiClient, options, shape) {
} else if (item.Type === 'Season' && item.ImageTags && item.ImageTags.Thumb) {
imgType = 'Thumb';
imgTag = item.ImageTags.Thumb;
- } else if (item.BackdropImageTags && item.BackdropImageTags.length) {
+ } else if (item.BackdropImageTags?.length) {
imgType = 'Backdrop';
imgTag = item.BackdropImageTags[0];
- } else if (item.ImageTags && item.ImageTags.Thumb) {
+ } else if (item.ImageTags?.Thumb) {
imgType = 'Thumb';
imgTag = item.ImageTags.Thumb;
} else if (item.SeriesThumbImageTag && options.inheritThumb !== false) {
@@ -605,7 +605,7 @@ function getCardImageUrl(item, apiClient, options, shape) {
imgType = 'Thumb';
imgTag = item.ParentThumbImageTag;
itemId = item.ParentThumbItemId;
- } else if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false) {
+ } else if (item.ParentBackdropImageTags?.length && options.inheritThumb !== false) {
imgType = 'Backdrop';
imgTag = item.ParentBackdropImageTags[0];
itemId = item.ParentBackdropItemId;
@@ -634,7 +634,7 @@ function getCardImageUrl(item, apiClient, options, shape) {
return {
imgUrl: imgUrl,
- blurhash: (blurHashes[imgType] || {})[imgTag],
+ blurhash: blurHashes[imgType]?.[imgTag],
forceName: forceName,
coverImage: coverImage
};
@@ -1422,7 +1422,7 @@ function buildCard(index, item, apiClient, options) {
className += ' card-withuserdata';
}
- const positionTicksData = item.UserData && item.UserData.PlaybackPositionTicks ? (' data-positionticks="' + item.UserData.PlaybackPositionTicks + '"') : '';
+ const positionTicksData = item.UserData?.PlaybackPositionTicks ? (' data-positionticks="' + item.UserData.PlaybackPositionTicks + '"') : '';
const collectionIdData = options.collectionId ? (' data-collectionid="' + options.collectionId + '"') : '';
const playlistIdData = options.playlistId ? (' data-playlistid="' + options.playlistId + '"') : '';
const mediaTypeData = item.MediaType ? (' data-mediatype="' + item.MediaType + '"') : '';
diff --git a/src/components/cardbuilder/chaptercardbuilder.js b/src/components/cardbuilder/chaptercardbuilder.js
index 4a359cd9f4..21535adc89 100644
--- a/src/components/cardbuilder/chaptercardbuilder.js
+++ b/src/components/cardbuilder/chaptercardbuilder.js
@@ -26,7 +26,7 @@ function buildChapterCardsHtml(item, chapters, options) {
}
}
- const mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || [];
+ const mediaStreams = (item.MediaSources || [])[0]?.MediaStreams || [];
const videoStream = mediaStreams.filter(({ Type }) => {
return Type === 'Video';
})[0] || {};
diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js
index 907de76b88..9384ba2056 100644
--- a/src/components/guide/guide.js
+++ b/src/components/guide/guide.js
@@ -675,12 +675,12 @@ function Guide(options) {
});
const activeElement = document.activeElement;
- const itemId = activeElement && activeElement.getAttribute ? activeElement.getAttribute('data-id') : null;
+ const itemId = activeElement?.getAttribute ? activeElement.getAttribute('data-id') : null;
let channelRowId = null;
if (activeElement) {
channelRowId = dom.parentWithClass(activeElement, 'channelPrograms');
- channelRowId = channelRowId && channelRowId.getAttribute ? channelRowId.getAttribute('data-channelid') : null;
+ channelRowId = channelRowId?.getAttribute ? channelRowId.getAttribute('data-channelid') : null;
}
renderChannelHeaders(context, channels, apiClient);
diff --git a/src/components/homesections/homesections.js b/src/components/homesections/homesections.js
index d5da017e2d..2bb9bb7928 100644
--- a/src/components/homesections/homesections.js
+++ b/src/components/homesections/homesections.js
@@ -76,7 +76,7 @@ export function loadSections(elem, apiClient, user, userSettings) {
});
} else {
let noLibDescription;
- if (user['Policy'] && user['Policy']['IsAdministrator']) {
+ if (user.Policy?.IsAdministrator) {
noLibDescription = globalize.translate('NoCreatedLibraries', '
', '');
} else {
noLibDescription = globalize.translate('AskAdminToCreateLibrary');
diff --git a/src/components/htmlMediaHelper.js b/src/components/htmlMediaHelper.js
index 124caa5e0a..f46885c79d 100644
--- a/src/components/htmlMediaHelper.js
+++ b/src/components/htmlMediaHelper.js
@@ -68,7 +68,7 @@ export function handleHlsJsMediaError(instance, reject) {
let now = Date.now();
- if (window.performance && window.performance.now) {
+ if (window.performance?.now) {
now = performance.now(); // eslint-disable-line compat/compat
}
diff --git a/src/components/imageUploader/imageUploader.js b/src/components/imageUploader/imageUploader.js
index c2c70d32ec..c016075ba2 100644
--- a/src/components/imageUploader/imageUploader.js
+++ b/src/components/imageUploader/imageUploader.js
@@ -41,7 +41,7 @@ function onFileReaderError(evt) {
function setFiles(page, files) {
const file = files[0];
- if (!file || !file.type.match('image.*')) {
+ if (!file?.type.match('image.*')) {
page.querySelector('#imageOutput').innerHTML = '';
page.querySelector('#fldUpload').classList.add('hide');
currentFile = null;
diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js
index beb4bb31a5..178312a314 100644
--- a/src/components/images/imageLoader.js
+++ b/src/components/images/imageLoader.js
@@ -9,7 +9,7 @@ worker.addEventListener(
'message',
({ data: { pixels, hsh, width, height } }) => {
const elems = targetDic[hsh];
- if (elems && elems.length) {
+ if (elems?.length) {
for (const elem of elems) {
drawBlurhash(elem, pixels, width, height);
}
diff --git a/src/components/indicators/indicators.js b/src/components/indicators/indicators.js
index 9abfffd8e1..ec21ae3e7f 100644
--- a/src/components/indicators/indicators.js
+++ b/src/components/indicators/indicators.js
@@ -12,7 +12,7 @@ export function enableProgressIndicator(item) {
export function getProgressHtml(pct, options) {
let containerClass = 'itemProgressBar';
- if (options && options.containerClass) {
+ if (options?.containerClass) {
containerClass += ' ' + options.containerClass;
}
@@ -21,7 +21,7 @@ export function getProgressHtml(pct, options) {
function getAutoTimeProgressHtml(pct, options, isRecording, start, end) {
let containerClass = 'itemProgressBar';
- if (options && options.containerClass) {
+ if (options?.containerClass) {
containerClass += ' ' + options.containerClass;
}
@@ -36,7 +36,7 @@ function getAutoTimeProgressHtml(pct, options, isRecording, start, end) {
export function getProgressBarHtml(item, options) {
let pct;
if (enableProgressIndicator(item) && item.Type !== 'Recording') {
- const userData = options && options.userData ? options.userData : item.UserData;
+ const userData = options?.userData ? options.userData : item.UserData;
if (userData) {
pct = userData.PlayedPercentage;
@@ -90,7 +90,7 @@ export function getPlayedIndicatorHtml(item) {
}
export function getChildCountIndicatorHtml(item, options) {
- const minCount = options && options.minCount ? options.minCount : 0;
+ const minCount = options?.minCount ? options.minCount : 0;
if (item.ChildCount && item.ChildCount > minCount) {
return '
' + globalize.translate('ValueContainer', profile.Container || allText) + '
'; - if (profile.Conditions && profile.Conditions.length) { + if (profile.Conditions?.length) { html += '