From cc87ba38593791e89dc39fc6deae256149c073a0 Mon Sep 17 00:00:00 2001 From: grafixeyehero Date: Wed, 31 Jan 2024 04:18:12 +0300 Subject: [PATCH] Add reusable component --- package-lock.json | 74 ++++++++++++++++++- package.json | 3 + src/components/common/DefaultIconText.tsx | 56 ++++++++++++++ src/components/common/DefaultName.tsx | 23 ++++++ src/components/common/Image.tsx | 67 +++++++++++++++++ src/components/common/InfoIconButton.tsx | 22 ++++++ src/components/common/Media.tsx | 36 +++++++++ src/components/common/MoreVertIconButton.tsx | 23 ++++++ src/components/common/NoItemsMessage.tsx | 25 +++++++ src/components/common/PlayArrowIconButton.tsx | 25 +++++++ .../common/PlaylistAddIconButton.tsx | 22 ++++++ src/components/common/RightIconButtons.tsx | 24 ++++++ src/types/dataAttributes.ts | 48 ++++++++++++ src/utils/image.ts | 37 +++++++++- src/utils/items.ts | 30 ++++++++ webpack.common.js | 2 + 16 files changed, 512 insertions(+), 5 deletions(-) create mode 100644 src/components/common/DefaultIconText.tsx create mode 100644 src/components/common/DefaultName.tsx create mode 100644 src/components/common/Image.tsx create mode 100644 src/components/common/InfoIconButton.tsx create mode 100644 src/components/common/Media.tsx create mode 100644 src/components/common/MoreVertIconButton.tsx create mode 100644 src/components/common/NoItemsMessage.tsx create mode 100644 src/components/common/PlayArrowIconButton.tsx create mode 100644 src/components/common/PlaylistAddIconButton.tsx create mode 100644 src/components/common/RightIconButtons.tsx create mode 100644 src/types/dataAttributes.ts diff --git a/package-lock.json b/package-lock.json index 18853390b0..559b3e61d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@react-hook/resize-observer": "1.2.6", "@tanstack/react-query": "4.36.1", "@tanstack/react-query-devtools": "4.36.1", + "@types/react-lazy-load-image-component": "1.6.3", "abortcontroller-polyfill": "1.7.5", "blurhash": "2.0.5", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", @@ -52,7 +53,9 @@ "native-promise-only": "0.8.1", "pdfjs-dist": "3.11.174", "react": "17.0.2", + "react-blurhash": "0.3.0", "react-dom": "17.0.2", + "react-lazy-load-image-component": "1.6.0", "react-router-dom": "6.21.3", "resize-observer-polyfill": "1.5.1", "screenfull": "6.0.2", @@ -4705,6 +4708,15 @@ "@types/react": "^17" } }, + "node_modules/@types/react-lazy-load-image-component": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@types/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.3.tgz", + "integrity": "sha512-HsIsYz7yWWTh/bftdzGnijKD26JyofLRqM/RM80sxs7Gk13G83ew8R/ra2XzXuiZfjNEjAq/Va+NBHFF9ciwxA==", + "dependencies": { + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/react-transition-group": { "version": "4.4.10", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", @@ -12671,8 +12683,7 @@ "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "node_modules/lodash.memoize": { "version": "4.1.2", @@ -12686,6 +12697,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -16202,6 +16218,15 @@ "node": ">=0.10.0" } }, + "node_modules/react-blurhash": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/react-blurhash/-/react-blurhash-0.3.0.tgz", + "integrity": "sha512-XlKr4Ns1iYFRnk6DkAblNbAwN/bTJvxTVoxMvmTcURdc5oLoXZwqAF9N3LZUh/HT+QFlq5n6IS6VsDGsviYAiQ==", + "peerDependencies": { + "blurhash": "^2.0.3", + "react": ">=15" + } + }, "node_modules/react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -16220,6 +16245,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-lazy-load-image-component": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.0.tgz", + "integrity": "sha512-8KFkDTgjh+0+PVbH+cx0AgxLGbdTsxWMnxXzU5HEUztqewk9ufQAu8cstjZhyvtMIPsdMcPZfA0WAa7HtjQbBQ==", + "dependencies": { + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1" + }, + "peerDependencies": { + "react": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x", + "react-dom": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x" + } + }, "node_modules/react-router": { "version": "6.21.3", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.3.tgz", @@ -25943,6 +25981,15 @@ "@types/react": "^17" } }, + "@types/react-lazy-load-image-component": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@types/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.3.tgz", + "integrity": "sha512-HsIsYz7yWWTh/bftdzGnijKD26JyofLRqM/RM80sxs7Gk13G83ew8R/ra2XzXuiZfjNEjAq/Va+NBHFF9ciwxA==", + "requires": { + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "@types/react-transition-group": { "version": "4.4.10", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", @@ -31866,8 +31913,7 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "lodash.memoize": { "version": "4.1.2", @@ -31881,6 +31927,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -34295,6 +34346,12 @@ "object-assign": "^4.1.1" } }, + "react-blurhash": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/react-blurhash/-/react-blurhash-0.3.0.tgz", + "integrity": "sha512-XlKr4Ns1iYFRnk6DkAblNbAwN/bTJvxTVoxMvmTcURdc5oLoXZwqAF9N3LZUh/HT+QFlq5n6IS6VsDGsviYAiQ==", + "requires": {} + }, "react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -34310,6 +34367,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lazy-load-image-component": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.0.tgz", + "integrity": "sha512-8KFkDTgjh+0+PVbH+cx0AgxLGbdTsxWMnxXzU5HEUztqewk9ufQAu8cstjZhyvtMIPsdMcPZfA0WAa7HtjQbBQ==", + "requires": { + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1" + } + }, "react-router": { "version": "6.21.3", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.3.tgz", diff --git a/package.json b/package.json index 9975e3f49f..aa6830dc2f 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "@react-hook/resize-observer": "1.2.6", "@tanstack/react-query": "4.36.1", "@tanstack/react-query-devtools": "4.36.1", + "@types/react-lazy-load-image-component": "1.6.3", "abortcontroller-polyfill": "1.7.5", "blurhash": "2.0.5", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", @@ -113,7 +114,9 @@ "native-promise-only": "0.8.1", "pdfjs-dist": "3.11.174", "react": "17.0.2", + "react-blurhash": "0.3.0", "react-dom": "17.0.2", + "react-lazy-load-image-component": "1.6.0", "react-router-dom": "6.21.3", "resize-observer-polyfill": "1.5.1", "screenfull": "6.0.2", diff --git a/src/components/common/DefaultIconText.tsx b/src/components/common/DefaultIconText.tsx new file mode 100644 index 0000000000..41f0014cb0 --- /dev/null +++ b/src/components/common/DefaultIconText.tsx @@ -0,0 +1,56 @@ +import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client'; +import React, { FC } from 'react'; +import Icon from '@mui/material/Icon'; +import imageHelper from 'utils/image'; +import DefaultName from './DefaultName'; +import type { ItemDto } from 'types/itemDto'; + +interface DefaultIconTextProps { + item: ItemDto; + defaultCardImageIcon?: string; +} + +const DefaultIconText: FC = ({ + item, + defaultCardImageIcon +}) => { + if (item.CollectionType) { + return ( + + ); + } + + if (item.Type && !(item.Type === BaseItemKind.TvChannel || item.Type === BaseItemKind.Studio )) { + return ( + + ); + } + + if (defaultCardImageIcon) { + return ( + + ); + } + + return ; +}; + +export default DefaultIconText; diff --git a/src/components/common/DefaultName.tsx b/src/components/common/DefaultName.tsx new file mode 100644 index 0000000000..5946fe27b5 --- /dev/null +++ b/src/components/common/DefaultName.tsx @@ -0,0 +1,23 @@ +import React, { FC } from 'react'; +import Box from '@mui/material/Box'; +import escapeHTML from 'escape-html'; +import itemHelper from 'components/itemHelper'; +import { isUsingLiveTvNaming } from '../cardbuilder/cardBuilderUtils'; +import type { ItemDto } from 'types/itemDto'; + +interface DefaultNameProps { + item: ItemDto; +} + +const DefaultName: FC = ({ item }) => { + const defaultName = isUsingLiveTvNaming(item.Type) ? + item.Name : + itemHelper.getDisplayName(item); + return ( + + {escapeHTML(defaultName)} + + ); +}; + +export default DefaultName; diff --git a/src/components/common/Image.tsx b/src/components/common/Image.tsx new file mode 100644 index 0000000000..14df552660 --- /dev/null +++ b/src/components/common/Image.tsx @@ -0,0 +1,67 @@ +import React, { FC, useCallback, useState } from 'react'; +import { BlurhashCanvas } from 'react-blurhash'; +import { LazyLoadImage } from 'react-lazy-load-image-component'; + +const imageStyle: React.CSSProperties = { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + width: '100%', + height: '100%', + zIndex: 0 +}; + +interface ImageProps { + imgUrl: string; + blurhash?: string; + containImage: boolean; +} + +const Image: FC = ({ + imgUrl, + blurhash, + containImage +}) => { + const [isLoaded, setIsLoaded] = useState(false); + const [isLoadStarted, setIsLoadStarted] = useState(false); + const handleLoad = useCallback(() => { + setIsLoaded(true); + }, []); + + const handleLoadStarted = useCallback(() => { + setIsLoadStarted(true); + }, []); + + return ( +
+ {!isLoaded && isLoadStarted && blurhash && ( + + )} + + +
+ ); +}; + +export default Image; diff --git a/src/components/common/InfoIconButton.tsx b/src/components/common/InfoIconButton.tsx new file mode 100644 index 0000000000..69c602e327 --- /dev/null +++ b/src/components/common/InfoIconButton.tsx @@ -0,0 +1,22 @@ +import React, { FC } from 'react'; +import IconButton from '@mui/material/IconButton'; +import InfoIcon from '@mui/icons-material/Info'; +import globalize from 'scripts/globalize'; + +interface InfoIconButtonProps { + className?: string; +} + +const InfoIconButton: FC = ({ className }) => { + return ( + + + + ); +}; + +export default InfoIconButton; diff --git a/src/components/common/Media.tsx b/src/components/common/Media.tsx new file mode 100644 index 0000000000..170208416f --- /dev/null +++ b/src/components/common/Media.tsx @@ -0,0 +1,36 @@ +import { BaseItemKind, ImageType } from '@jellyfin/sdk/lib/generated-client'; +import React, { FC } from 'react'; +import Image from './Image'; +import DefaultIconText from './DefaultIconText'; +import type { ItemDto } from 'types/itemDto'; + +interface MediaProps { + item: ItemDto; + imgUrl: string | undefined; + blurhash: string | undefined; + imageType?: ImageType + defaultCardImageIcon?: string +} + +const Media: FC = ({ + item, + imgUrl, + blurhash, + imageType, + defaultCardImageIcon +}) => { + return imgUrl ? ( + + ) : ( + + ); +}; + +export default Media; diff --git a/src/components/common/MoreVertIconButton.tsx b/src/components/common/MoreVertIconButton.tsx new file mode 100644 index 0000000000..231a2afed1 --- /dev/null +++ b/src/components/common/MoreVertIconButton.tsx @@ -0,0 +1,23 @@ +import React, { FC } from 'react'; +import IconButton from '@mui/material/IconButton'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import globalize from 'scripts/globalize'; + +interface MoreVertIconButtonProps { + className?: string; + iconClassName?: string; +} + +const MoreVertIconButton: FC = ({ className, iconClassName }) => { + return ( + + + + ); +}; + +export default MoreVertIconButton; diff --git a/src/components/common/NoItemsMessage.tsx b/src/components/common/NoItemsMessage.tsx new file mode 100644 index 0000000000..2c59b0ed6b --- /dev/null +++ b/src/components/common/NoItemsMessage.tsx @@ -0,0 +1,25 @@ +import React, { FC } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import globalize from 'scripts/globalize'; + +interface NoItemsMessageProps { + noItemsMessage?: string; +} + +const NoItemsMessage: FC = ({ + noItemsMessage = 'MessageNoItemsAvailable' +}) => { + return ( + + + {globalize.translate('MessageNothingHere')} + + + {globalize.translate(noItemsMessage)} + + + ); +}; + +export default NoItemsMessage; diff --git a/src/components/common/PlayArrowIconButton.tsx b/src/components/common/PlayArrowIconButton.tsx new file mode 100644 index 0000000000..b64fd9bd05 --- /dev/null +++ b/src/components/common/PlayArrowIconButton.tsx @@ -0,0 +1,25 @@ +import React, { FC } from 'react'; +import IconButton from '@mui/material/IconButton'; +import PlayArrowIcon from '@mui/icons-material/PlayArrow'; +import globalize from 'scripts/globalize'; + +interface PlayArrowIconButtonProps { + className: string; + action: string; + title: string; + iconClassName?: string; +} + +const PlayArrowIconButton: FC = ({ className, action, title, iconClassName }) => { + return ( + + + + ); +}; + +export default PlayArrowIconButton; diff --git a/src/components/common/PlaylistAddIconButton.tsx b/src/components/common/PlaylistAddIconButton.tsx new file mode 100644 index 0000000000..19469e0fe3 --- /dev/null +++ b/src/components/common/PlaylistAddIconButton.tsx @@ -0,0 +1,22 @@ +import React, { FC } from 'react'; +import IconButton from '@mui/material/IconButton'; +import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd'; +import globalize from 'scripts/globalize'; + +interface PlaylistAddIconButtonProps { + className?: string; +} + +const PlaylistAddIconButton: FC = ({ className }) => { + return ( + + + + ); +}; + +export default PlaylistAddIconButton; diff --git a/src/components/common/RightIconButtons.tsx b/src/components/common/RightIconButtons.tsx new file mode 100644 index 0000000000..2787a1856c --- /dev/null +++ b/src/components/common/RightIconButtons.tsx @@ -0,0 +1,24 @@ +import React, { FC } from 'react'; +import IconButton from '@mui/material/IconButton'; + +interface RightIconButtonsProps { + className?: string; + id: string; + icon: string; + title: string; +} + +const RightIconButtons: FC = ({ className, id, title, icon }) => { + return ( + + {icon} + + ); +}; + +export default RightIconButtons; diff --git a/src/types/dataAttributes.ts b/src/types/dataAttributes.ts new file mode 100644 index 0000000000..6e91b125cd --- /dev/null +++ b/src/types/dataAttributes.ts @@ -0,0 +1,48 @@ +import type { CollectionType, UserItemDataDto } from '@jellyfin/sdk/lib/generated-client'; +import type { NullableBoolean, NullableNumber, NullableString } from './itemDto'; + +export type AttributesOpts = { + context?: CollectionType | undefined, + parentId?: NullableString, + collectionId?: NullableString, + playlistId?: NullableString, + prefix?: NullableString, + action?: NullableString, + itemServerId?: NullableString, + itemId?: NullableString, + itemTimerId?: NullableString, + itemSeriesTimerId?: NullableString, + itemChannelId?: NullableString, + itemPlaylistItemId?: NullableString, + itemType?: NullableString, + itemMediaType?: NullableString, + itemCollectionType?: NullableString, + itemIsFolder?: NullableBoolean, + itemPath?: NullableString, + itemStartDate?: NullableString, + itemEndDate?: NullableString, + itemUserData?: UserItemDataDto +}; + +export type DataAttributes = { + 'data-playlistitemid'?: NullableString; + 'data-timerid'?: NullableString; + 'data-seriestimerid'?: NullableString; + 'data-serverid'?: NullableString; + 'data-id'?: NullableString; + 'data-type'?: NullableString; + 'data-collectionid'?: NullableString; + 'data-playlistid'?: NullableString; + 'data-mediatype'?: NullableString; + 'data-channelid'?: NullableString; + 'data-path'?: NullableString; + 'data-collectiontype'?: NullableString; + 'data-context'?: NullableString; + 'data-parentid'?: NullableString; + 'data-startdate'?: NullableString; + 'data-enddate'?: NullableString; + 'data-prefix'?: NullableString; + 'data-action'?: NullableString; + 'data-positionticks'?: NullableNumber; + 'data-isfolder'?: NullableBoolean; +}; diff --git a/src/utils/image.ts b/src/utils/image.ts index 7420fad41c..3819f865df 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -1,3 +1,4 @@ +import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client'; import type { DeviceInfo } from '@jellyfin/sdk/lib/generated-client/models/device-info'; import type { SessionInfo } from '@jellyfin/sdk/lib/generated-client/models/session-info'; @@ -103,7 +104,41 @@ export function getLibraryIcon(library: string | null | undefined) { } } +export function getItemTypeIcon(itemType: BaseItemKind | string) { + switch (itemType) { + case BaseItemKind.MusicAlbum: + return 'album'; + case BaseItemKind.MusicArtist: + case BaseItemKind.Person: + return 'person'; + case BaseItemKind.Audio: + return 'audiotrack'; + case BaseItemKind.Movie: + return 'movie'; + case BaseItemKind.Episode: + case BaseItemKind.Series: + return 'tv'; + case BaseItemKind.Program: + return 'live_tv'; + case BaseItemKind.Book: + return 'book'; + case BaseItemKind.Folder: + return 'folder'; + case BaseItemKind.BoxSet: + return 'collections'; + case BaseItemKind.Playlist: + return 'view_list'; + case BaseItemKind.Photo: + return 'photo'; + case BaseItemKind.PhotoAlbum: + return 'photo_album'; + default: + return 'folder'; + } +} + export default { getDeviceIcon, - getLibraryIcon + getLibraryIcon, + getItemTypeIcon }; diff --git a/src/utils/items.ts b/src/utils/items.ts index 752226db26..6a5dcd0985 100644 --- a/src/utils/items.ts +++ b/src/utils/items.ts @@ -3,8 +3,10 @@ import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type' import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by'; import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order'; import * as userSettings from 'scripts/settings/userSettings'; +import layoutManager from 'components/layoutManager'; import { EpisodeFilter, FeatureFilters, LibraryViewSettings, ParentId, VideoBasicFilter, ViewMode } from '../types/library'; import { LibraryTab } from 'types/libraryTab'; +import type { AttributesOpts, DataAttributes } from 'types/dataAttributes'; export const getVideoBasicFilter = (libraryViewSettings: LibraryViewSettings) => { let isHd; @@ -164,3 +166,31 @@ export const getDefaultLibraryViewSettings = (viewType: LibraryTab): LibraryView StartIndex: 0 }; }; + +export function getDataAttributes( + opts: AttributesOpts +): DataAttributes { + return { + 'data-context': opts.context, + 'data-collectionid': opts.collectionId, + 'data-playlistid': opts.playlistId, + 'data-parentid': opts.parentId, + 'data-playlistitemid': opts.itemPlaylistItemId, + 'data-action': layoutManager.tv ? opts.action : null, + 'data-serverid': opts.itemServerId, + 'data-id': opts.itemId, + 'data-timerid': opts.itemTimerId, + 'data-seriestimerid': opts.itemSeriesTimerId, + 'data-channelid': opts.itemChannelId, + 'data-type': opts.itemType, + 'data-mediatype': opts.itemMediaType, + 'data-collectiontype': opts.itemCollectionType, + 'data-isfolder': opts.itemIsFolder, + 'data-path': opts.itemPath, + 'data-prefix': opts.prefix, + 'data-positionticks': opts.itemUserData?.PlaybackPositionTicks, + 'data-startdate': opts.itemStartDate?.toString(), + 'data-enddate': opts.itemEndDate?.toString() + }; +} + diff --git a/webpack.common.js b/webpack.common.js index dd85d03183..1c4587e87b 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -200,6 +200,8 @@ const config = { path.resolve(__dirname, 'node_modules/markdown-it'), path.resolve(__dirname, 'node_modules/mdurl'), path.resolve(__dirname, 'node_modules/punycode'), + path.resolve(__dirname, 'node_modules/react-blurhash'), + path.resolve(__dirname, 'node_modules/react-lazy-load-image-component'), path.resolve(__dirname, 'node_modules/react-router'), path.resolve(__dirname, 'node_modules/screenfull'), path.resolve(__dirname, 'node_modules/ssr-window'),