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++) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ]);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>';
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -43,5 +43,5 @@ export function decimalCount(value: number): number {
|
|||
|
||||
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