mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Fix unchecked index access issues
This commit is contained in:
parent
173d05da8f
commit
422fe86e02
17 changed files with 60 additions and 41 deletions
|
@ -79,10 +79,12 @@ const UserParentalControl = () => {
|
||||||
for (let i = 0, length = allParentalRatings.length; i < length; i++) {
|
for (let i = 0, length = allParentalRatings.length; i < length; i++) {
|
||||||
rating = allParentalRatings[i];
|
rating = allParentalRatings[i];
|
||||||
|
|
||||||
|
if (!rating) continue;
|
||||||
|
|
||||||
if (ratings.length) {
|
if (ratings.length) {
|
||||||
const lastRating = ratings[ratings.length - 1];
|
const lastRating = ratings[ratings.length - 1];
|
||||||
|
|
||||||
if (lastRating.Value === rating.Value) {
|
if (lastRating && lastRating.Value === rating.Value) {
|
||||||
lastRating.Name += '/' + rating.Name;
|
lastRating.Name += '/' + rating.Name;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,15 @@ function playAllFromHere(opts: PlayAllFromHereOptions) {
|
||||||
let startIndex;
|
let startIndex;
|
||||||
|
|
||||||
for (let i = 0, length = items?.length; i < length; i++) {
|
for (let i = 0, length = items?.length; i < length; i++) {
|
||||||
|
if (!items[i]) continue;
|
||||||
|
|
||||||
if (items[i] === item) {
|
if (items[i] === item) {
|
||||||
foundCard = true;
|
foundCard = true;
|
||||||
startIndex = i;
|
startIndex = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundCard || !queue) {
|
if (foundCard || !queue) {
|
||||||
ids.push(items[i].Id);
|
ids.push(items[i]?.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,15 +67,13 @@ const Home = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return import(/* webpackChunkName: "[request]" */ `../../../controllers/${depends}`).then(({ default: ControllerFactory }) => {
|
return import(/* webpackChunkName: "[request]" */ `../../../controllers/${depends}`).then(({ default: ControllerFactory }) => {
|
||||||
let controller = tabControllers[index];
|
const controller = tabControllers[index];
|
||||||
|
if (controller) return controller;
|
||||||
|
|
||||||
if (!controller) {
|
|
||||||
const tabContent = element.current?.querySelector(".tabContent[data-index='" + index + "']");
|
const tabContent = element.current?.querySelector(".tabContent[data-index='" + index + "']");
|
||||||
controller = new ControllerFactory(tabContent, null);
|
const newController = new ControllerFactory(tabContent, null);
|
||||||
tabControllers[index] = controller;
|
tabControllers[index] = newController;
|
||||||
}
|
return newController;
|
||||||
|
|
||||||
return controller;
|
|
||||||
});
|
});
|
||||||
}, [ tabControllers ]);
|
}, [ tabControllers ]);
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ const homevideosTabMapping: LibraryTabMapping = {
|
||||||
|
|
||||||
const HomeVideos: FC = () => {
|
const HomeVideos: FC = () => {
|
||||||
const { libraryId, activeTab } = useCurrentTab();
|
const { libraryId, activeTab } = useCurrentTab();
|
||||||
const currentTab = homevideosTabMapping[activeTab];
|
const currentTab = homevideosTabMapping[activeTab] || photosTabContent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
|
|
|
@ -52,7 +52,7 @@ const liveTvTabMapping: LibraryTabMapping = {
|
||||||
|
|
||||||
const LiveTv: FC = () => {
|
const LiveTv: FC = () => {
|
||||||
const { libraryId, activeTab } = useCurrentTab();
|
const { libraryId, activeTab } = useCurrentTab();
|
||||||
const currentTab = liveTvTabMapping[activeTab];
|
const currentTab = liveTvTabMapping[activeTab] || programsTabContent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
|
|
|
@ -60,7 +60,7 @@ const moviesTabMapping: LibraryTabMapping = {
|
||||||
|
|
||||||
const Movies: FC = () => {
|
const Movies: FC = () => {
|
||||||
const { libraryId, activeTab } = useCurrentTab();
|
const { libraryId, activeTab } = useCurrentTab();
|
||||||
const currentTab = moviesTabMapping[activeTab];
|
const currentTab = moviesTabMapping[activeTab] || moviesTabContent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
|
|
|
@ -68,7 +68,7 @@ const musicTabMapping: LibraryTabMapping = {
|
||||||
|
|
||||||
const Music: FC = () => {
|
const Music: FC = () => {
|
||||||
const { libraryId, activeTab } = useCurrentTab();
|
const { libraryId, activeTab } = useCurrentTab();
|
||||||
const currentTab = musicTabMapping[activeTab];
|
const currentTab = musicTabMapping[activeTab] || albumsTabContent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
|
|
|
@ -59,7 +59,7 @@ const tvShowsTabMapping: LibraryTabMapping = {
|
||||||
|
|
||||||
const Shows: FC = () => {
|
const Shows: FC = () => {
|
||||||
const { libraryId, activeTab } = useCurrentTab();
|
const { libraryId, activeTab } = useCurrentTab();
|
||||||
const currentTab = tvShowsTabMapping[activeTab];
|
const currentTab = tvShowsTabMapping[activeTab] || seriesTabContent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
|
|
|
@ -124,7 +124,7 @@ class MediaSegmentManager extends PlaybackSubscriber {
|
||||||
const currentSegmentDetails = findCurrentSegment(this.mediaSegments, time, this.lastSegmentIndex);
|
const currentSegmentDetails = findCurrentSegment(this.mediaSegments, time, this.lastSegmentIndex);
|
||||||
if (
|
if (
|
||||||
// The current time falls within a segment
|
// The current time falls within a segment
|
||||||
currentSegmentDetails
|
currentSegmentDetails?.segment
|
||||||
// and the last segment is not ignored or the segment index has changed
|
// and the last segment is not ignored or the segment index has changed
|
||||||
&& (!this.isLastSegmentIgnored || this.lastSegmentIndex !== currentSegmentDetails.index)
|
&& (!this.isLastSegmentIgnored || this.lastSegmentIndex !== currentSegmentDetails.index)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import type { MediaSegmentDto } from '@jellyfin/sdk/lib/generated-client/models/media-segment-dto';
|
import type { MediaSegmentDto } from '@jellyfin/sdk/lib/generated-client/models/media-segment-dto';
|
||||||
|
|
||||||
const isBeforeSegment = (segment: MediaSegmentDto, time: number, direction: number) => {
|
const isBeforeSegment = (segment: MediaSegmentDto | undefined, time: number, direction: number) => {
|
||||||
if (direction === -1) {
|
if (direction === -1) {
|
||||||
return (
|
return (
|
||||||
typeof segment.EndTicks !== 'undefined'
|
typeof segment?.EndTicks !== 'undefined'
|
||||||
&& segment.EndTicks <= time
|
&& segment.EndTicks <= time
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
typeof segment.StartTicks !== 'undefined'
|
typeof segment?.StartTicks !== 'undefined'
|
||||||
&& segment.StartTicks > time
|
&& segment.StartTicks > time
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isInSegment = (segment: MediaSegmentDto, time: number) => (
|
export const isInSegment = (segment: MediaSegmentDto | undefined, time: number) => (
|
||||||
typeof segment.StartTicks !== 'undefined'
|
typeof segment?.StartTicks !== 'undefined'
|
||||||
&& segment.StartTicks <= time
|
&& segment.StartTicks <= time
|
||||||
&& (typeof segment.EndTicks === 'undefined' || segment.EndTicks > time)
|
&& (typeof segment.EndTicks === 'undefined' || segment.EndTicks > time)
|
||||||
);
|
);
|
||||||
|
@ -26,7 +26,7 @@ export const findCurrentSegment = (segments: MediaSegmentDto[], time: number, la
|
||||||
}
|
}
|
||||||
|
|
||||||
let direction = 1;
|
let direction = 1;
|
||||||
if (lastIndex > 0 && lastSegment.StartTicks && lastSegment.StartTicks > time) {
|
if (lastIndex > 0 && lastSegment?.StartTicks && lastSegment.StartTicks > time) {
|
||||||
direction = -1;
|
direction = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,8 @@ function getPosition(positionTo: Element, options: Options, dlg: HTMLElement) {
|
||||||
|
|
||||||
const pos = getOffsets([positionTo])[0];
|
const pos = getOffsets([positionTo])[0];
|
||||||
|
|
||||||
|
if (!pos) return;
|
||||||
|
|
||||||
if (options.positionY !== 'top') {
|
if (options.positionY !== 'top') {
|
||||||
pos.top += (pos.height || 0) / 2;
|
pos.top += (pos.height || 0) / 2;
|
||||||
}
|
}
|
||||||
|
@ -250,6 +252,8 @@ export function show(options: Options) {
|
||||||
for (let i = 0; i < options.items.length; i++) {
|
for (let i = 0; i < options.items.length; i++) {
|
||||||
const item = options.items[i];
|
const item = options.items[i];
|
||||||
|
|
||||||
|
if (!item) continue;
|
||||||
|
|
||||||
if (item.divider) {
|
if (item.divider) {
|
||||||
html += '<div class="actionsheetDivider"></div>';
|
html += '<div class="actionsheetDivider"></div>';
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -209,7 +209,7 @@ function getParentTitle(
|
||||||
serverId: NullableString,
|
serverId: NullableString,
|
||||||
item: ItemDto
|
item: ItemDto
|
||||||
) {
|
) {
|
||||||
if (isOuterFooter && item.AlbumArtists?.length) {
|
if (isOuterFooter && item.AlbumArtists?.length && item.AlbumArtists[0]) {
|
||||||
(item.AlbumArtists[0] as ItemDto).Type = ItemKind.MusicArtist;
|
(item.AlbumArtists[0] as ItemDto).Type = ItemKind.MusicArtist;
|
||||||
(item.AlbumArtists[0] as ItemDto).IsFolder = true;
|
(item.AlbumArtists[0] as ItemDto).IsFolder = true;
|
||||||
return getTextActionButton(item.AlbumArtists[0], null, serverId);
|
return getTextActionButton(item.AlbumArtists[0], null, serverId);
|
||||||
|
|
|
@ -17,8 +17,8 @@ function getLibraryButtonsHtml(items: BaseItemDto[]) {
|
||||||
// library card background images
|
// library card background images
|
||||||
for (let i = 0, length = items.length; i < length; i++) {
|
for (let i = 0, length = items.length; i < length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
const icon = imageHelper.getLibraryIcon(item.CollectionType);
|
const icon = imageHelper.getLibraryIcon(item?.CollectionType);
|
||||||
html += '<a is="emby-linkbutton" href="' + appRouter.getRouteUrl(item) + '" class="raised homeLibraryButton"><span class="material-icons homeLibraryIcon ' + icon + '" aria-hidden="true"></span><span class="homeLibraryText">' + escapeHtml(item.Name) + '</span></a>';
|
html += '<a is="emby-linkbutton" href="' + appRouter.getRouteUrl(item) + '" class="raised homeLibraryButton"><span class="material-icons homeLibraryIcon ' + icon + '" aria-hidden="true"></span><span class="homeLibraryText">' + escapeHtml(item?.Name || '') + '</span></a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
|
@ -12,7 +12,9 @@ const sortBySortName = (item: ItemDto): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SortName
|
// SortName
|
||||||
const name = (item.SortName ?? item.Name ?? '?')[0].toUpperCase();
|
const name = (item.SortName ?? item.Name ?? '?')[0]?.toUpperCase();
|
||||||
|
|
||||||
|
if (!name) return '';
|
||||||
|
|
||||||
const code = name.charCodeAt(0);
|
const code = name.charCodeAt(0);
|
||||||
if (code < 65 || code > 90) {
|
if (code < 65 || code > 90) {
|
||||||
|
@ -48,7 +50,9 @@ const sortByAlbumArtist = (item: ItemDto): string => {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = item.AlbumArtist[0].toUpperCase();
|
const name = item.AlbumArtist[0]?.toUpperCase();
|
||||||
|
|
||||||
|
if (!name) return '';
|
||||||
|
|
||||||
const code = name.charCodeAt(0);
|
const code = name.charCodeAt(0);
|
||||||
if (code < 65 || code > 90) {
|
if (code < 65 || code > 90) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ function getFirstAndLastVisible(scrollFrame: HTMLElement, items: HTMLElement[],
|
||||||
|
|
||||||
const currentScrollPos = scrollPosition * localeModifier;
|
const currentScrollPos = scrollPosition * localeModifier;
|
||||||
const scrollerWidth = scrollFrame.offsetWidth;
|
const scrollerWidth = scrollFrame.offsetWidth;
|
||||||
const itemWidth = items[0].offsetWidth;
|
const itemWidth = items[0]?.offsetWidth || 1;
|
||||||
|
|
||||||
// Rounding down here will give us the first item index which is fully visible. We want the first partially visible
|
// Rounding down here will give us the first item index which is fully visible. We want the first partially visible
|
||||||
// index so we'll subtract one.
|
// index so we'll subtract one.
|
||||||
|
@ -53,7 +53,7 @@ function getFirstAndLastVisible(scrollFrame: HTMLElement, items: HTMLElement[],
|
||||||
// Rounding up will give us the last index which is at least partially visible (overflows at container end).
|
// Rounding up will give us the last index which is at least partially visible (overflows at container end).
|
||||||
const lastVisibleIndex = Math.floor((currentScrollPos + scrollerWidth) / itemWidth);
|
const lastVisibleIndex = Math.floor((currentScrollPos + scrollerWidth) / itemWidth);
|
||||||
|
|
||||||
return [firstVisibleIndex, lastVisibleIndex];
|
return { firstVisibleIndex, lastVisibleIndex };
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToWindow({
|
function scrollToWindow({
|
||||||
|
@ -71,18 +71,22 @@ function scrollToWindow({
|
||||||
// factory functions on it, but is not a true scroller factory. For legacy, we need to pass `scroller` directly
|
// factory functions on it, but is not a true scroller factory. For legacy, we need to pass `scroller` directly
|
||||||
// instead of getting the frame from the factory instance.
|
// instead of getting the frame from the factory instance.
|
||||||
const frame = scroller.getScrollFrame?.() ?? scroller;
|
const frame = scroller.getScrollFrame?.() ?? scroller;
|
||||||
const [firstVisibleIndex, lastVisibleIndex] = getFirstAndLastVisible(frame, items, scrollState);
|
const { firstVisibleIndex, lastVisibleIndex } = getFirstAndLastVisible(frame, items, scrollState);
|
||||||
|
|
||||||
let scrollToPosition: number;
|
let scrollToPosition = 0;
|
||||||
|
|
||||||
if (direction === ScrollDirection.RIGHT) {
|
if (direction === ScrollDirection.RIGHT) {
|
||||||
const nextItem = items[lastVisibleIndex];
|
const nextItem = items[lastVisibleIndex];
|
||||||
|
|
||||||
|
if (nextItem) {
|
||||||
// This will be the position to anchor the item at `lastVisibleIndex` to the start of the view window.
|
// This will be the position to anchor the item at `lastVisibleIndex` to the start of the view window.
|
||||||
const nextItemScrollOffset = lastVisibleIndex * nextItem.offsetWidth;
|
const nextItemScrollOffset = lastVisibleIndex * nextItem.offsetWidth;
|
||||||
scrollToPosition = nextItemScrollOffset * localeModifier;
|
scrollToPosition = nextItemScrollOffset * localeModifier;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const previousItem = items[firstVisibleIndex];
|
const previousItem = items[firstVisibleIndex];
|
||||||
|
|
||||||
|
if (previousItem) {
|
||||||
const previousItemScrollOffset = firstVisibleIndex * previousItem.offsetWidth;
|
const previousItemScrollOffset = firstVisibleIndex * previousItem.offsetWidth;
|
||||||
|
|
||||||
// Find the total number of items that can fit in a view window and subtract one to account for item at
|
// Find the total number of items that can fit in a view window and subtract one to account for item at
|
||||||
|
@ -93,6 +97,7 @@ function scrollToWindow({
|
||||||
// This will be the position to anchor the item at `firstVisibleIndex` to the end of the view window.
|
// This will be the position to anchor the item at `firstVisibleIndex` to the end of the view window.
|
||||||
scrollToPosition = (previousItemScrollOffset - offsetAdjustment) * localeModifier;
|
scrollToPosition = (previousItemScrollOffset - offsetAdjustment) * localeModifier;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (scroller.slideTo) {
|
if (scroller.slideTo) {
|
||||||
scroller.slideTo(scrollToPosition, false, undefined);
|
scroller.slideTo(scrollToPosition, false, undefined);
|
||||||
|
|
|
@ -7,6 +7,9 @@ export function readFileAsBase64(file: File): Promise<string> {
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
// Split by a comma to remove the url: prefix
|
// Split by a comma to remove the url: prefix
|
||||||
const data = (e.target?.result as string)?.split?.(',')[1];
|
const data = (e.target?.result as string)?.split?.(',')[1];
|
||||||
|
if (!data) {
|
||||||
|
return reject(new Error('No file data'));
|
||||||
|
}
|
||||||
resolve(data);
|
resolve(data);
|
||||||
};
|
};
|
||||||
reader.onerror = reject;
|
reader.onerror = reject;
|
||||||
|
|
|
@ -43,5 +43,5 @@ export function decimalCount(value: number): number {
|
||||||
|
|
||||||
const arr = value.toString().split('.');
|
const arr = value.toString().split('.');
|
||||||
|
|
||||||
return arr[1].length;
|
return arr[1]?.length || 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue