1
0
Fork 0
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:
Bill Thornton 2025-01-12 03:51:53 -05:00
parent 173d05da8f
commit 422fe86e02
17 changed files with 60 additions and 41 deletions

View file

@ -79,10 +79,12 @@ const UserParentalControl = () => {
for (let i = 0, length = allParentalRatings.length; i < length; i++) {
rating = allParentalRatings[i];
if (!rating) continue;
if (ratings.length) {
const lastRating = ratings[ratings.length - 1];
if (lastRating.Value === rating.Value) {
if (lastRating && lastRating.Value === rating.Value) {
lastRating.Name += '/' + rating.Name;
continue;
}

View file

@ -29,12 +29,15 @@ function playAllFromHere(opts: PlayAllFromHereOptions) {
let startIndex;
for (let i = 0, length = items?.length; i < length; i++) {
if (!items[i]) continue;
if (items[i] === item) {
foundCard = true;
startIndex = i;
}
if (foundCard || !queue) {
ids.push(items[i].Id);
ids.push(items[i]?.Id);
}
}

View file

@ -67,15 +67,13 @@ const Home = () => {
}
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 + "']");
controller = new ControllerFactory(tabContent, null);
tabControllers[index] = controller;
}
return controller;
const tabContent = element.current?.querySelector(".tabContent[data-index='" + index + "']");
const newController = new ControllerFactory(tabContent, null);
tabControllers[index] = newController;
return newController;
});
}, [ tabControllers ]);

View file

@ -39,7 +39,7 @@ const homevideosTabMapping: LibraryTabMapping = {
const HomeVideos: FC = () => {
const { libraryId, activeTab } = useCurrentTab();
const currentTab = homevideosTabMapping[activeTab];
const currentTab = homevideosTabMapping[activeTab] || photosTabContent;
return (
<Page

View file

@ -52,7 +52,7 @@ const liveTvTabMapping: LibraryTabMapping = {
const LiveTv: FC = () => {
const { libraryId, activeTab } = useCurrentTab();
const currentTab = liveTvTabMapping[activeTab];
const currentTab = liveTvTabMapping[activeTab] || programsTabContent;
return (
<Page

View file

@ -60,7 +60,7 @@ const moviesTabMapping: LibraryTabMapping = {
const Movies: FC = () => {
const { libraryId, activeTab } = useCurrentTab();
const currentTab = moviesTabMapping[activeTab];
const currentTab = moviesTabMapping[activeTab] || moviesTabContent;
return (
<Page

View file

@ -68,7 +68,7 @@ const musicTabMapping: LibraryTabMapping = {
const Music: FC = () => {
const { libraryId, activeTab } = useCurrentTab();
const currentTab = musicTabMapping[activeTab];
const currentTab = musicTabMapping[activeTab] || albumsTabContent;
return (
<Page

View file

@ -59,7 +59,7 @@ const tvShowsTabMapping: LibraryTabMapping = {
const Shows: FC = () => {
const { libraryId, activeTab } = useCurrentTab();
const currentTab = tvShowsTabMapping[activeTab];
const currentTab = tvShowsTabMapping[activeTab] || seriesTabContent;
return (
<Page

View file

@ -124,7 +124,7 @@ class MediaSegmentManager extends PlaybackSubscriber {
const currentSegmentDetails = findCurrentSegment(this.mediaSegments, time, this.lastSegmentIndex);
if (
// The current time falls within a segment
currentSegmentDetails
currentSegmentDetails?.segment
// and the last segment is not ignored or the segment index has changed
&& (!this.isLastSegmentIgnored || this.lastSegmentIndex !== currentSegmentDetails.index)
) {

View file

@ -1,20 +1,20 @@
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) {
return (
typeof segment.EndTicks !== 'undefined'
typeof segment?.EndTicks !== 'undefined'
&& segment.EndTicks <= time
);
}
return (
typeof segment.StartTicks !== 'undefined'
typeof segment?.StartTicks !== 'undefined'
&& segment.StartTicks > time
);
};
export const isInSegment = (segment: MediaSegmentDto, time: number) => (
typeof segment.StartTicks !== 'undefined'
export const isInSegment = (segment: MediaSegmentDto | undefined, time: number) => (
typeof segment?.StartTicks !== 'undefined'
&& segment.StartTicks <= time
&& (typeof segment.EndTicks === 'undefined' || segment.EndTicks > time)
);
@ -26,7 +26,7 @@ export const findCurrentSegment = (segments: MediaSegmentDto[], time: number, la
}
let direction = 1;
if (lastIndex > 0 && lastSegment.StartTicks && lastSegment.StartTicks > time) {
if (lastIndex > 0 && lastSegment?.StartTicks && lastSegment.StartTicks > time) {
direction = -1;
}

View file

@ -93,6 +93,8 @@ function getPosition(positionTo: Element, options: Options, dlg: HTMLElement) {
const pos = getOffsets([positionTo])[0];
if (!pos) return;
if (options.positionY !== 'top') {
pos.top += (pos.height || 0) / 2;
}
@ -250,6 +252,8 @@ export function show(options: Options) {
for (let i = 0; i < options.items.length; i++) {
const item = options.items[i];
if (!item) continue;
if (item.divider) {
html += '<div class="actionsheetDivider"></div>';
continue;

View file

@ -209,7 +209,7 @@ function getParentTitle(
serverId: NullableString,
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).IsFolder = true;
return getTextActionButton(item.AlbumArtists[0], null, serverId);

View file

@ -17,8 +17,8 @@ function getLibraryButtonsHtml(items: BaseItemDto[]) {
// library card background images
for (let i = 0, length = items.length; i < length; i++) {
const item = items[i];
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>';
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 += '</div>';

View file

@ -12,7 +12,9 @@ const sortBySortName = (item: ItemDto): string => {
}
// 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);
if (code < 65 || code > 90) {
@ -48,7 +50,9 @@ const sortByAlbumArtist = (item: ItemDto): string => {
return '';
}
const name = item.AlbumArtist[0].toUpperCase();
const name = item.AlbumArtist[0]?.toUpperCase();
if (!name) return '';
const code = name.charCodeAt(0);
if (code < 65 || code > 90) {

View file

@ -45,7 +45,7 @@ function getFirstAndLastVisible(scrollFrame: HTMLElement, items: HTMLElement[],
const currentScrollPos = scrollPosition * localeModifier;
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
// 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).
const lastVisibleIndex = Math.floor((currentScrollPos + scrollerWidth) / itemWidth);
return [firstVisibleIndex, lastVisibleIndex];
return { firstVisibleIndex, lastVisibleIndex };
}
function scrollToWindow({
@ -71,27 +71,32 @@ function scrollToWindow({
// 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.
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) {
const nextItem = items[lastVisibleIndex];
if (nextItem) {
// This will be the position to anchor the item at `lastVisibleIndex` to the start of the view window.
const nextItemScrollOffset = lastVisibleIndex * nextItem.offsetWidth;
scrollToPosition = nextItemScrollOffset * localeModifier;
const nextItemScrollOffset = lastVisibleIndex * nextItem.offsetWidth;
scrollToPosition = nextItemScrollOffset * localeModifier;
}
} else {
const previousItem = items[firstVisibleIndex];
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
// `firstVisibleIndex`. The total width of these items is the amount that we need to adjust the scroll position by
// to anchor item at `firstVisibleIndex` to the end of the view window.
const offsetAdjustment = (Math.floor(frame.offsetWidth / previousItem.offsetWidth) - 1) * previousItem.offsetWidth;
if (previousItem) {
const previousItemScrollOffset = firstVisibleIndex * previousItem.offsetWidth;
// This will be the position to anchor the item at `firstVisibleIndex` to the end of the view window.
scrollToPosition = (previousItemScrollOffset - offsetAdjustment) * localeModifier;
// Find the total number of items that can fit in a view window and subtract one to account for item at
// `firstVisibleIndex`. The total width of these items is the amount that we need to adjust the scroll position by
// to anchor item at `firstVisibleIndex` to the end of the view window.
const offsetAdjustment = (Math.floor(frame.offsetWidth / previousItem.offsetWidth) - 1) * previousItem.offsetWidth;
// This will be the position to anchor the item at `firstVisibleIndex` to the end of the view window.
scrollToPosition = (previousItemScrollOffset - offsetAdjustment) * localeModifier;
}
}
if (scroller.slideTo) {

View file

@ -7,6 +7,9 @@ export function readFileAsBase64(file: File): Promise<string> {
reader.onload = (e) => {
// Split by a comma to remove the url: prefix
const data = (e.target?.result as string)?.split?.(',')[1];
if (!data) {
return reject(new Error('No file data'));
}
resolve(data);
};
reader.onerror = reject;

View file

@ -43,5 +43,5 @@ export function decimalCount(value: number): number {
const arr = value.toString().split('.');
return arr[1].length;
return arr[1]?.length || 0;
}