diff --git a/server/src/dao/programHelpers.ts b/server/src/dao/programHelpers.ts index df497e7d0..49e1f2d42 100644 --- a/server/src/dao/programHelpers.ts +++ b/server/src/dao/programHelpers.ts @@ -46,7 +46,7 @@ import { } from '../util/index.js'; import { ProgramMinterFactory } from '../util/programMinter.js'; import { ProgramSourceType } from './custom_types/ProgramSourceType.js'; -import { getEm, withDb } from './dataSource.js'; +import { getEm } from './dataSource.js'; import { PlexServerSettings } from './entities/PlexServerSettings.js'; import { Program, ProgramType } from './entities/Program.js'; import { @@ -123,20 +123,25 @@ export async function upsertContentPrograms( .mapValues((programs) => groupBy(programs, (p) => p.externalSourceName!)) .value() as ProgramsBySource; + logger.debug('Upserting %d programs', programsToPersist.length); + + const upsertedPrograms = flatten( + await mapAsyncSeq(chunk(programsToPersist, batchSize), (programs) => + em.upsertMany(Program, programs, { + onConflictAction: 'merge', + onConflictFields: ['sourceType', 'externalSourceId', 'externalKey'], + onConflictExcludeFields: ['uuid'], + }), + ), + ); + // Fork a new entity manager here so we don't attempt to persist anything // in the parent context. This function potentially does a lot of work // but we don't want to accidentally not do an upsert of a program. - const programGroupingsBySource = await withDb( - async () => { - return await findAndUpdateProgramRelations(programsBySource); - }, - undefined, - true, - ); + const programGroupingsBySource = + await findAndUpdateProgramRelations(programsBySource); - logger.debug('Upserting %d programs', programsToPersist.length); - - forEach(programsToPersist, (program) => { + forEach(upsertedPrograms, (program) => { if ( program.type !== ProgramType.Episode && program.type !== ProgramType.Track @@ -215,15 +220,7 @@ export async function upsertContentPrograms( } }); - const upsertedPrograms = flatten( - await mapAsyncSeq(chunk(programsToPersist, batchSize), (programs) => - em.upsertMany(Program, programs, { - onConflictAction: 'merge', - onConflictFields: ['sourceType', 'externalSourceId', 'externalKey'], - onConflictExcludeFields: ['uuid'], - }), - ), - ); + await em.flush(); return upsertedPrograms; } @@ -289,9 +286,9 @@ async function findAndUpdatePlexServerPrograms( return; } - const em = getEm(); + const em = getEm().fork(); - const plexServer = await getEm().findOne(PlexServerSettings, { + const plexServer = await em.findOne(PlexServerSettings, { name: plexServerName, }); diff --git a/web/src/components/InlineModal.tsx b/web/src/components/InlineModal.tsx index bcdd5eec3..3e162a34f 100644 --- a/web/src/components/InlineModal.tsx +++ b/web/src/components/InlineModal.tsx @@ -1,13 +1,7 @@ import { Box, Collapse, List } from '@mui/material'; -import { PlexMedia, isPlexMedia, isTerminalItem } from '@tunarr/types/plex'; +import { PlexMedia, isPlexMedia, isPlexParentItem } from '@tunarr/types/plex'; import { usePrevious } from '@uidotdev/usehooks'; -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { useEffect, useCallback, useRef, useState, useMemo } from 'react'; import { extractLastIndexes, findFirstItemInNextRowIndex, @@ -17,8 +11,9 @@ import { import _ from 'lodash-es'; import useStore from '../store'; import { PlexGridItem } from './channel_config/PlexGridItem'; -import { useIntersectionObserver } from 'usehooks-ts'; import { toggle } from '../helpers/util.ts'; +import React from 'react'; +import { useIntersectionObserver } from 'usehooks-ts'; type InlineModalProps = { itemGuid: string; @@ -76,7 +71,6 @@ export function InlineModal(props: InlineModalProps) { const containerWidth = ref?.current?.getBoundingClientRect().width || 0; const itemWidth = gridItemRef?.current?.getBoundingClientRect().width || 0; - const imagesPerRow = getImagesPerRow(containerWidth, itemWidth); setChildLimit(imagesPerRow * 2); setItemWidth(itemWidth); @@ -85,24 +79,29 @@ export function InlineModal(props: InlineModalProps) { } }, [ref, gridItemRef, previousItemGuid, itemGuid]); + const [childItemGuid, setChildItemGuid] = useState(null); const [childModalIndex, setChildModalIndex] = useState(-1); - const handleMoveModal = useCallback( - (index: number) => { - if (index === childModalIndex) { - setChildModalIndex(-1); - } else { - setChildModalIndex(index); - } - }, - [childModalIndex], + const firstItemInNextRowIndex = useMemo( + () => + findFirstItemInNextRowIndex( + childModalIndex, + rowSize, + modalChildren?.length ?? 0, + ), + [childModalIndex, modalChildren?.length, rowSize], ); + const handleMoveModal = useCallback((index: number, item: PlexMedia) => { + setChildItemGuid((prev) => (prev === item.guid ? null : item.guid)); + setChildModalIndex((prev) => (prev === index ? -1 : index)); + }, []); + // TODO: Complete this by updating the limit below, not doing this // right now because already working with a huge changeset. const { ref: intersectionRef } = useIntersectionObserver({ onChange: (_, entry) => { - if (entry.isIntersecting) { + if (entry.isIntersecting && isOpen) { if (childLimit < modalChildren.length) { setChildLimit((prev) => prev + imagesPerRow * 2); } @@ -118,6 +117,8 @@ export function InlineModal(props: InlineModalProps) { ).includes(childModalIndex) : false; + console.log('my children, should be open', type, modalChildren); + return ( ( - {!isTerminalItem(child) && ( + {isPlexParentItem(child) && ( @@ -183,7 +177,7 @@ export function InlineModal(props: InlineModalProps) { @@ -192,7 +186,7 @@ export function InlineModal(props: InlineModalProps) { .value()} {/* This Modal is for last row items because they can't be inserted using the above inline modal */} { if (!isUndefined(children?.Metadata)) { + console.log(item.guid, children.Metadata); addKnownMediaForServer(server.name, children.Metadata, item.guid); } }, [item.guid, server.name, children]); diff --git a/web/src/components/channel_config/PlexProgrammingSelector.tsx b/web/src/components/channel_config/PlexProgrammingSelector.tsx index 9a7e0946f..5f6956751 100644 --- a/web/src/components/channel_config/PlexProgrammingSelector.tsx +++ b/web/src/components/channel_config/PlexProgrammingSelector.tsx @@ -138,7 +138,9 @@ export default function PlexProgrammingSelector() { const imageWidth = imageRef?.getBoundingClientRect().width; // 16 is additional padding available in the parent container - setRowSize(getImagesPerRow(width ? width + 16 : 0, imageWidth || 0)); + const rowSize = getImagesPerRow(width ? width + 16 : 0, imageWidth || 0); + setRowSize(rowSize); + setScrollParams(({ max }) => ({ max, limit: rowSize * 4 })); } }, [width, tabValue, viewType, modalGuid]); @@ -319,19 +321,24 @@ export default function PlexProgrammingSelector() { }); useEffect(() => { - if (!isUndefined(searchData)) { - // We're using this as an analogue for detecting the start of a new 'query' - if (searchData.pages.length === 1) { - const max = searchData.pages[0].totalSize ?? searchData.pages[0].size; - setScrollParams({ - limit: rowSize * 4, - max, - }); + if (searchData?.pages.length === 1) { + const size = searchData.pages[0].totalSize ?? searchData.pages[0].size; + if (scrollParams.max !== size) { + console.log('herehereh'); + setScrollParams(({ limit }) => ({ + limit, + max: size, + })); } + } + }, [searchData?.pages, scrollParams.max]); + useEffect(() => { + if (!isUndefined(searchData)) { // We probably wouldn't have made it this far if we didnt have a server, but // putting this here to prevent crashes if (selectedServer) { + console.log('still in this one'); const allMedia = chain(searchData.pages) .reject((page) => page.size === 0) .map((page) => page.Metadata) @@ -340,7 +347,7 @@ export default function PlexProgrammingSelector() { addKnownMediaForServer(selectedServer.name, allMedia); } } - }, [selectedServer, searchData, setScrollParams, rowSize]); + }, [scrollParams, selectedServer, searchData, rowSize]); useEffect(() => { if ( @@ -407,7 +414,6 @@ export default function PlexProgrammingSelector() { (tabValue === 0 ? firstItemInNexLibraryRowIndex : firstItemInNextCollectionRowIndex); - return ( {isPlexParentItem(item) && ( diff --git a/web/src/helpers/inlineModalUtil.ts b/web/src/helpers/inlineModalUtil.ts index 9ba39db2c..1dae37189 100644 --- a/web/src/helpers/inlineModalUtil.ts +++ b/web/src/helpers/inlineModalUtil.ts @@ -112,5 +112,10 @@ export function findFirstItemInNextRowIndex( } export function extractLastIndexes(arr: PlexMedia[], x: number): number[] { - return range(x > arr.length ? 0 : x, arr.length); + const indexes = range(0, arr.length); + if (x > arr.length) { + return indexes; + } + + return indexes.slice(-x); } diff --git a/web/src/store/programmingSelector/actions.ts b/web/src/store/programmingSelector/actions.ts index d70a663f1..03da53b8a 100644 --- a/web/src/store/programmingSelector/actions.ts +++ b/web/src/store/programmingSelector/actions.ts @@ -4,7 +4,6 @@ import { PlexLibrarySection, PlexMedia, isPlexDirectory, - isTerminalItem, } from '@tunarr/types/plex'; import { map, reject, some, uniq } from 'lodash-es'; import useStore from '..'; @@ -68,20 +67,23 @@ export const addKnownMediaForServer = ( hierarchy = state.contentHierarchyByServer[serverName]; } - const childrenByGuid = plexMedia - .filter((m) => !isTerminalItem(m)) - .reduce((prev, media) => ({ ...prev, [uniqueId(media)]: [] }), {}); - - state.contentHierarchyByServer[serverName] = { - ...state.contentHierarchyByServer[serverName], - ...childrenByGuid, - }; + // plexMedia + // .filter((m) => !isTerminalItem(m)) + // .map(uniqueId) + // .forEach((id) => { + // if (!has(state.contentHierarchyByServer[serverName], id)) { + // state.contentHierarchyByServer[serverName][id] = []; + // } + // }); if (parentId) { + console.log('setting parent id', parentId); if (!state.contentHierarchyByServer[serverName][parentId]) { state.contentHierarchyByServer[serverName][parentId] = []; } + console.log(plexMedia.map(uniqueId)); + state.contentHierarchyByServer[serverName][parentId] = uniq([ ...state.contentHierarchyByServer[serverName][parentId], ...plexMedia.map(uniqueId),