Use enum for item actions
This commit is contained in:
@@ -9,6 +9,7 @@ import useMediaQuery from '@mui/material/useMediaQuery';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { type FC, useCallback } from 'react';
|
import React, { type FC, useCallback } from 'react';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import { useApi } from 'hooks/useApi';
|
import { useApi } from 'hooks/useApi';
|
||||||
import { useLocalStorage } from 'hooks/useLocalStorage';
|
import { useLocalStorage } from 'hooks/useLocalStorage';
|
||||||
import { useGetItemsViewByType } from 'hooks/useFetchItems';
|
import { useGetItemsViewByType } from 'hooks/useFetchItems';
|
||||||
@@ -99,7 +100,7 @@ const ItemsView: FC<ItemsViewProps> = ({
|
|||||||
|
|
||||||
if (viewType === LibraryTab.Songs) {
|
if (viewType === LibraryTab.Songs) {
|
||||||
listOptions.showParentTitle = true;
|
listOptions.showParentTitle = true;
|
||||||
listOptions.action = 'playallfromhere';
|
listOptions.action = ItemAction.PlayAllFromHere;
|
||||||
listOptions.smallIcon = true;
|
listOptions.smallIcon = true;
|
||||||
listOptions.showArtist = true;
|
listOptions.showArtist = true;
|
||||||
listOptions.addToListButton = true;
|
listOptions.addToListButton = true;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
import { useApi } from 'hooks/useApi';
|
|
||||||
import { useGetProgramsSectionsWithItems, useGetTimers } from 'hooks/useFetchItems';
|
|
||||||
import { appRouter } from 'components/router/appRouter';
|
|
||||||
import globalize from 'lib/globalize';
|
|
||||||
import Loading from 'components/loading/LoadingComponent';
|
|
||||||
import NoItemsMessage from 'components/common/NoItemsMessage';
|
import NoItemsMessage from 'components/common/NoItemsMessage';
|
||||||
import SectionContainer from 'components/common/SectionContainer';
|
import SectionContainer from 'components/common/SectionContainer';
|
||||||
import { CardShape } from 'utils/card';
|
import Loading from 'components/loading/LoadingComponent';
|
||||||
|
import { appRouter } from 'components/router/appRouter';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
import { useApi } from 'hooks/useApi';
|
||||||
|
import { useGetProgramsSectionsWithItems, useGetTimers } from 'hooks/useFetchItems';
|
||||||
|
import globalize from 'lib/globalize';
|
||||||
import type { ParentId } from 'types/library';
|
import type { ParentId } from 'types/library';
|
||||||
import type { Section, SectionType } from 'types/sections';
|
import type { Section, SectionType } from 'types/sections';
|
||||||
|
import { CardShape } from 'utils/card';
|
||||||
|
|
||||||
interface ProgramsSectionViewProps {
|
interface ProgramsSectionViewProps {
|
||||||
parentId: ParentId;
|
parentId: ParentId;
|
||||||
@@ -92,7 +94,7 @@ const ProgramsSectionView: FC<ProgramsSectionViewProps> = ({
|
|||||||
showChannelName: false,
|
showChannelName: false,
|
||||||
cardLayout: true,
|
cardLayout: true,
|
||||||
centerText: false,
|
centerText: false,
|
||||||
action: 'edit',
|
action: ItemAction.Edit,
|
||||||
cardFooterAside: 'none',
|
cardFooterAside: 'none',
|
||||||
preferThumb: true,
|
preferThumb: true,
|
||||||
coverImage: true,
|
coverImage: true,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
|||||||
import ReplayIcon from '@mui/icons-material/Replay';
|
import ReplayIcon from '@mui/icons-material/Replay';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import { useApi } from 'hooks/useApi';
|
import { useApi } from 'hooks/useApi';
|
||||||
import { getChannelQuery } from 'hooks/api/liveTvHooks/useGetChannel';
|
import { getChannelQuery } from 'hooks/api/liveTvHooks/useGetChannel';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
@@ -76,7 +77,7 @@ const PlayOrResumeButton: FC<PlayOrResumeButtonProps> = ({
|
|||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
className='button-flat btnPlayOrResume'
|
className='button-flat btnPlayOrResume'
|
||||||
data-action={isResumable ? 'resume' : 'play'}
|
data-action={isResumable ? ItemAction.Resume : ItemAction.Play}
|
||||||
title={
|
title={
|
||||||
isResumable ?
|
isResumable ?
|
||||||
globalize.translate('ButtonResume') :
|
globalize.translate('ButtonResume') :
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
|
|
||||||
import layoutManager from 'components/layoutManager';
|
import layoutManager from 'components/layoutManager';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
import { CardShape } from 'utils/card';
|
||||||
|
import type { ItemDto } from 'types/base/models/item-dto';
|
||||||
|
import type { CardOptions } from 'types/cardOptions';
|
||||||
|
|
||||||
import CardOverlayButtons from './CardOverlayButtons';
|
import CardOverlayButtons from './CardOverlayButtons';
|
||||||
import CardHoverMenu from './CardHoverMenu';
|
import CardHoverMenu from './CardHoverMenu';
|
||||||
import CardOuterFooter from './CardOuterFooter';
|
import CardOuterFooter from './CardOuterFooter';
|
||||||
import CardContent from './CardContent';
|
import CardContent from './CardContent';
|
||||||
import { CardShape } from 'utils/card';
|
|
||||||
|
|
||||||
import type { ItemDto } from 'types/base/models/item-dto';
|
|
||||||
import type { CardOptions } from 'types/cardOptions';
|
|
||||||
|
|
||||||
interface CardBoxProps {
|
interface CardBoxProps {
|
||||||
action: string;
|
action: ItemAction;
|
||||||
item: ItemDto;
|
item: ItemDto;
|
||||||
cardOptions: CardOptions;
|
cardOptions: CardOptions;
|
||||||
className: string;
|
className: string;
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import React, { type FC } from 'react';
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import ButtonGroup from '@mui/material/ButtonGroup';
|
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { appRouter } from 'components/router/appRouter';
|
import { appRouter } from 'components/router/appRouter';
|
||||||
import itemHelper from 'components/itemHelper';
|
import itemHelper from 'components/itemHelper';
|
||||||
import { playbackManager } from 'components/playback/playbackmanager';
|
import { playbackManager } from 'components/playback/playbackmanager';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import PlayedButton from 'elements/emby-playstatebutton/PlayedButton';
|
import PlayedButton from 'elements/emby-playstatebutton/PlayedButton';
|
||||||
import FavoriteButton from 'elements/emby-ratingbutton/FavoriteButton';
|
import FavoriteButton from 'elements/emby-ratingbutton/FavoriteButton';
|
||||||
|
|
||||||
import PlayArrowIconButton from '../../common/PlayArrowIconButton';
|
import PlayArrowIconButton from '../../common/PlayArrowIconButton';
|
||||||
import MoreVertIconButton from '../../common/MoreVertIconButton';
|
import MoreVertIconButton from '../../common/MoreVertIconButton';
|
||||||
|
|
||||||
@@ -15,7 +17,7 @@ import type { ItemDto } from 'types/base/models/item-dto';
|
|||||||
import type { CardOptions } from 'types/cardOptions';
|
import type { CardOptions } from 'types/cardOptions';
|
||||||
|
|
||||||
interface CardHoverMenuProps {
|
interface CardHoverMenuProps {
|
||||||
action: string,
|
action: ItemAction,
|
||||||
item: ItemDto;
|
item: ItemDto;
|
||||||
cardOptions: CardOptions;
|
cardOptions: CardOptions;
|
||||||
}
|
}
|
||||||
@@ -51,7 +53,7 @@ const CardHoverMenu: FC<CardHoverMenuProps> = ({
|
|||||||
{playbackManager.canPlay(item) && (
|
{playbackManager.canPlay(item) && (
|
||||||
<PlayArrowIconButton
|
<PlayArrowIconButton
|
||||||
className={centerPlayButtonClass}
|
className={centerPlayButtonClass}
|
||||||
action='play'
|
action={ItemAction.Play}
|
||||||
title='Play'
|
title='Play'
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,15 +2,17 @@ import { LocationType } from '@jellyfin/sdk/lib/generated-client/models/location
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
import ButtonGroup from '@mui/material/ButtonGroup';
|
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { appRouter } from 'components/router/appRouter';
|
|
||||||
import PlayArrowIconButton from '../../common/PlayArrowIconButton';
|
|
||||||
import MoreVertIconButton from '../../common/MoreVertIconButton';
|
|
||||||
|
|
||||||
|
import { appRouter } from 'components/router/appRouter';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import { ItemKind } from 'types/base/models/item-kind';
|
import { ItemKind } from 'types/base/models/item-kind';
|
||||||
import { ItemMediaKind } from 'types/base/models/item-media-kind';
|
import { ItemMediaKind } from 'types/base/models/item-media-kind';
|
||||||
import type { ItemDto } from 'types/base/models/item-dto';
|
import type { ItemDto } from 'types/base/models/item-dto';
|
||||||
import type { CardOptions } from 'types/cardOptions';
|
import type { CardOptions } from 'types/cardOptions';
|
||||||
|
|
||||||
|
import PlayArrowIconButton from '../../common/PlayArrowIconButton';
|
||||||
|
import MoreVertIconButton from '../../common/MoreVertIconButton';
|
||||||
|
|
||||||
const sholudShowOverlayPlayButton = (
|
const sholudShowOverlayPlayButton = (
|
||||||
overlayPlayButton: boolean | undefined,
|
overlayPlayButton: boolean | undefined,
|
||||||
item: ItemDto
|
item: ItemDto
|
||||||
@@ -78,7 +80,7 @@ const CardOverlayButtons: FC<CardOverlayButtonsProps> = ({
|
|||||||
{cardOptions.centerPlayButton && (
|
{cardOptions.centerPlayButton && (
|
||||||
<PlayArrowIconButton
|
<PlayArrowIconButton
|
||||||
className={centerPlayButtonClass}
|
className={centerPlayButtonClass}
|
||||||
action='play'
|
action={ItemAction.Play}
|
||||||
title='Play'
|
title='Play'
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -87,7 +89,7 @@ const CardOverlayButtons: FC<CardOverlayButtonsProps> = ({
|
|||||||
{sholudShowOverlayPlayButton(overlayPlayButton, item) && (
|
{sholudShowOverlayPlayButton(overlayPlayButton, item) && (
|
||||||
<PlayArrowIconButton
|
<PlayArrowIconButton
|
||||||
className={btnCssClass}
|
className={btnCssClass}
|
||||||
action='play'
|
action={ItemAction.Play}
|
||||||
title='Play'
|
title='Play'
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api';
|
|||||||
import { appRouter } from 'components/router/appRouter';
|
import { appRouter } from 'components/router/appRouter';
|
||||||
import layoutManager from 'components/layoutManager';
|
import layoutManager from 'components/layoutManager';
|
||||||
import itemHelper from 'components/itemHelper';
|
import itemHelper from 'components/itemHelper';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
import datetime from 'scripts/datetime';
|
import datetime from 'scripts/datetime';
|
||||||
import { isUsingLiveTvNaming } from '../cardBuilderUtils';
|
import { isUsingLiveTvNaming } from '../cardBuilderUtils';
|
||||||
@@ -88,7 +89,7 @@ export function getTextActionButton(
|
|||||||
|
|
||||||
const dataAttributes = getDataAttributes(
|
const dataAttributes = getDataAttributes(
|
||||||
{
|
{
|
||||||
action: 'link',
|
action: ItemAction.Link,
|
||||||
itemServerId: serverId ?? item.ServerId,
|
itemServerId: serverId ?? item.ServerId,
|
||||||
itemId: item.Id,
|
itemId: item.Id,
|
||||||
itemChannelId: item.ChannelId,
|
itemChannelId: item.ChannelId,
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import layoutManager from 'components/layoutManager';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
import { ItemKind } from 'types/base/models/item-kind';
|
||||||
|
import { ItemMediaKind } from 'types/base/models/item-media-kind';
|
||||||
|
import type { ItemDto } from 'types/base/models/item-dto';
|
||||||
|
import type { CardOptions } from 'types/cardOptions';
|
||||||
|
import { CardShape } from 'utils/card';
|
||||||
|
import { getDataAttributes } from 'utils/items';
|
||||||
|
|
||||||
import useCardImageUrl from './useCardImageUrl';
|
import useCardImageUrl from './useCardImageUrl';
|
||||||
import {
|
import {
|
||||||
resolveAction,
|
resolveAction,
|
||||||
resolveMixedShapeByAspectRatio
|
resolveMixedShapeByAspectRatio
|
||||||
} from '../cardBuilderUtils';
|
} from '../cardBuilderUtils';
|
||||||
import { getDataAttributes } from 'utils/items';
|
|
||||||
import { CardShape } from 'utils/card';
|
|
||||||
import layoutManager from 'components/layoutManager';
|
|
||||||
|
|
||||||
import { ItemKind } from 'types/base/models/item-kind';
|
|
||||||
import { ItemMediaKind } from 'types/base/models/item-media-kind';
|
|
||||||
import type { ItemDto } from 'types/base/models/item-dto';
|
|
||||||
import type { CardOptions } from 'types/cardOptions';
|
|
||||||
|
|
||||||
interface UseCardProps {
|
interface UseCardProps {
|
||||||
item: ItemDto;
|
item: ItemDto;
|
||||||
@@ -20,7 +22,7 @@ interface UseCardProps {
|
|||||||
|
|
||||||
function useCard({ item, cardOptions }: UseCardProps) {
|
function useCard({ item, cardOptions }: UseCardProps) {
|
||||||
const action = resolveAction({
|
const action = resolveAction({
|
||||||
defaultAction: cardOptions.action ?? 'link',
|
defaultAction: cardOptions.action ?? ItemAction.Link,
|
||||||
isFolder: item.IsFolder ?? false,
|
isFolder: item.IsFolder ?? false,
|
||||||
isPhoto: item.MediaType === ItemMediaKind.Photo
|
isPhoto: item.MediaType === ItemMediaKind.Photo
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-ite
|
|||||||
import { PersonKind } from '@jellyfin/sdk/lib/generated-client/models/person-kind';
|
import { PersonKind } from '@jellyfin/sdk/lib/generated-client/models/person-kind';
|
||||||
import escapeHtml from 'escape-html';
|
import escapeHtml from 'escape-html';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import browser from 'scripts/browser';
|
import browser from 'scripts/browser';
|
||||||
import datetime from 'scripts/datetime';
|
import datetime from 'scripts/datetime';
|
||||||
import dom from 'utils/dom';
|
import dom from 'utils/dom';
|
||||||
@@ -514,7 +515,7 @@ function getCardFooterText(item, apiClient, options, footerClass, progressHtml,
|
|||||||
const showOtherText = flags.isOuterFooter ? !flags.overlayText : flags.overlayText;
|
const showOtherText = flags.isOuterFooter ? !flags.overlayText : flags.overlayText;
|
||||||
|
|
||||||
if (flags.isOuterFooter && options.cardLayout && layoutManager.mobile && options.cardFooterAside !== 'none') {
|
if (flags.isOuterFooter && options.cardLayout && layoutManager.mobile && options.cardFooterAside !== 'none') {
|
||||||
html += `<button is="paper-icon-button-light" class="itemAction btnCardOptions cardText-secondary" data-action="menu" title="${globalize.translate('ButtonMore')}"><span class="material-icons more_vert" aria-hidden="true"></span></button>`;
|
html += `<button is="paper-icon-button-light" class="itemAction btnCardOptions cardText-secondary" data-action="${ItemAction.Menu}" title="${globalize.translate('ButtonMore')}"><span class="material-icons more_vert" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cssClass = options.centerText ? 'cardText cardTextCentered' : 'cardText';
|
const cssClass = options.centerText ? 'cardText cardTextCentered' : 'cardText';
|
||||||
@@ -776,7 +777,7 @@ function getTextActionButton(item, text, serverId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = appRouter.getRouteUrl(item);
|
const url = appRouter.getRouteUrl(item);
|
||||||
let html = '<a href="' + url + '" ' + itemShortcuts.getShortcutAttributesHtml(item, serverId) + ' class="itemAction textActionButton" title="' + text + '" data-action="link">';
|
let html = '<a href="' + url + '" ' + itemShortcuts.getShortcutAttributesHtml(item, serverId) + ' class="itemAction textActionButton" title="' + text + `" data-action="${ItemAction.Link}">`;
|
||||||
html += text;
|
html += text;
|
||||||
html += '</a>';
|
html += '</a>';
|
||||||
|
|
||||||
@@ -885,7 +886,7 @@ function importRefreshIndicator() {
|
|||||||
*/
|
*/
|
||||||
function buildCard(index, item, apiClient, options) {
|
function buildCard(index, item, apiClient, options) {
|
||||||
const action = resolveAction({
|
const action = resolveAction({
|
||||||
defaultAction: options.action || 'link',
|
defaultAction: options.action || ItemAction.Link,
|
||||||
isFolder: item.IsFolder,
|
isFolder: item.IsFolder,
|
||||||
isPhoto: item.MediaType === 'Photo'
|
isPhoto: item.MediaType === 'Photo'
|
||||||
});
|
});
|
||||||
@@ -985,15 +986,15 @@ function buildCard(index, item, apiClient, options) {
|
|||||||
const btnCssClass = 'cardOverlayButton cardOverlayButton-br itemAction';
|
const btnCssClass = 'cardOverlayButton cardOverlayButton-br itemAction';
|
||||||
|
|
||||||
if (options.centerPlayButton) {
|
if (options.centerPlayButton) {
|
||||||
overlayButtons += `<button is="paper-icon-button-light" class="${btnCssClass} cardOverlayButton-centered" data-action="play" title="${globalize.translate('Play')}"><span class="material-icons cardOverlayButtonIcon play_arrow" aria-hidden="true"></span></button>`;
|
overlayButtons += `<button is="paper-icon-button-light" class="${btnCssClass} cardOverlayButton-centered" data-action="${ItemAction.Play}" title="${globalize.translate('Play')}"><span class="material-icons cardOverlayButtonIcon play_arrow" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overlayPlayButton && !item.IsPlaceHolder && (item.LocationType !== 'Virtual' || !item.MediaType || item.Type === 'Program') && item.Type !== 'Person') {
|
if (overlayPlayButton && !item.IsPlaceHolder && (item.LocationType !== 'Virtual' || !item.MediaType || item.Type === 'Program') && item.Type !== 'Person') {
|
||||||
overlayButtons += `<button is="paper-icon-button-light" class="${btnCssClass}" data-action="play" title="${globalize.translate('Play')}"><span class="material-icons cardOverlayButtonIcon play_arrow" aria-hidden="true"></span></button>`;
|
overlayButtons += `<button is="paper-icon-button-light" class="${btnCssClass}" data-action="${ItemAction.Play}" title="${globalize.translate('Play')}"><span class="material-icons cardOverlayButtonIcon play_arrow" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.overlayMoreButton) {
|
if (options.overlayMoreButton) {
|
||||||
overlayButtons += `<button is="paper-icon-button-light" class="${btnCssClass}" data-action="menu" title="${globalize.translate('ButtonMore')}"><span class="material-icons cardOverlayButtonIcon more_vert" aria-hidden="true"></span></button>`;
|
overlayButtons += `<button is="paper-icon-button-light" class="${btnCssClass}" data-action="${ItemAction.Menu}" title="${globalize.translate('ButtonMore')}"><span class="material-icons cardOverlayButtonIcon more_vert" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1156,7 +1157,7 @@ function getHoverMenuHtml(item, action) {
|
|||||||
const btnCssClass = 'cardOverlayButton cardOverlayButton-hover itemAction paper-icon-button-light';
|
const btnCssClass = 'cardOverlayButton cardOverlayButton-hover itemAction paper-icon-button-light';
|
||||||
|
|
||||||
if (playbackManager.canPlay(item)) {
|
if (playbackManager.canPlay(item)) {
|
||||||
html += '<button is="paper-icon-button-light" class="' + btnCssClass + ' cardOverlayFab-primary" data-action="resume"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover play_arrow" aria-hidden="true"></span></button>';
|
html += `<button is="paper-icon-button-light" class="${btnCssClass} cardOverlayFab-primary" data-action="${ItemAction.Resume}"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover play_arrow" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '<div class="cardOverlayButton-br flex">';
|
html += '<div class="cardOverlayButton-br flex">';
|
||||||
@@ -1165,17 +1166,17 @@ function getHoverMenuHtml(item, action) {
|
|||||||
|
|
||||||
if (itemHelper.canMarkPlayed(item)) {
|
if (itemHelper.canMarkPlayed(item)) {
|
||||||
import('../../elements/emby-playstatebutton/emby-playstatebutton');
|
import('../../elements/emby-playstatebutton/emby-playstatebutton');
|
||||||
html += '<button is="emby-playstatebutton" type="button" data-action="none" class="' + btnCssClass + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-played="' + (userData.Played) + '"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover check" aria-hidden="true"></span></button>';
|
html += `<button is="emby-playstatebutton" type="button" data-action="${ItemAction.None}" class="${btnCssClass}" data-id="${item.Id}" data-serverid="${item.ServerId}" data-itemtype="${item.Type}" data-played="${userData.Played}"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover check" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemHelper.canRate(item)) {
|
if (itemHelper.canRate(item)) {
|
||||||
const likes = userData.Likes == null ? '' : userData.Likes;
|
const likes = userData.Likes == null ? '' : userData.Likes;
|
||||||
|
|
||||||
import('../../elements/emby-ratingbutton/emby-ratingbutton');
|
import('../../elements/emby-ratingbutton/emby-ratingbutton');
|
||||||
html += '<button is="emby-ratingbutton" type="button" data-action="none" class="' + btnCssClass + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-likes="' + likes + '" data-isfavorite="' + (userData.IsFavorite) + '"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover favorite" aria-hidden="true"></span></button>';
|
html += `<button is="emby-ratingbutton" type="button" data-action="${ItemAction.None}" class="${btnCssClass}" data-id="${item.Id}" data-serverid="${item.ServerId}" data-itemtype="${item.Type}" data-likes="${likes}" data-isfavorite="${userData.IsFavorite}"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover favorite" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
html += `<button is="paper-icon-button-light" class="${btnCssClass}" data-action="menu" title="${globalize.translate('ButtonMore')}"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover more_vert" aria-hidden="true"></span></button>`;
|
html += `<button is="paper-icon-button-light" class="${btnCssClass}" data-action="${ItemAction.Menu}" title="${globalize.translate('ButtonMore')}"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover more_vert" aria-hidden="true"></span></button>`;
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
resolveCardImageContainerCssClasses,
|
resolveCardImageContainerCssClasses,
|
||||||
resolveMixedShapeByAspectRatio
|
resolveMixedShapeByAspectRatio
|
||||||
} from './cardBuilderUtils';
|
} from './cardBuilderUtils';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
|
||||||
describe('getDesiredAspect', () => {
|
describe('getDesiredAspect', () => {
|
||||||
test('"portrait" (case insensitive)', () => {
|
test('"portrait" (case insensitive)', () => {
|
||||||
@@ -441,11 +442,11 @@ describe('isResizable', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('resolveAction', () => {
|
describe('resolveAction', () => {
|
||||||
test('default action', () => expect(resolveAction({ defaultAction: 'link', isFolder: false, isPhoto: false })).toEqual('link'));
|
test('default action', () => expect(resolveAction({ defaultAction: ItemAction.Link, isFolder: false, isPhoto: false })).toEqual(ItemAction.Link));
|
||||||
|
|
||||||
test('photo', () => expect(resolveAction({ defaultAction: 'link', isFolder: false, isPhoto: true })).toEqual('play'));
|
test('photo', () => expect(resolveAction({ defaultAction: ItemAction.Link, isFolder: false, isPhoto: true })).toEqual(ItemAction.Play));
|
||||||
|
|
||||||
test('default action is "play" and is folder', () => expect(resolveAction({ defaultAction: 'play', isFolder: true, isPhoto: true })).toEqual('link'));
|
test('default action is "play" and is folder', () => expect(resolveAction({ defaultAction: ItemAction.Play, isFolder: true, isPhoto: true })).toEqual(ItemAction.Link));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('resolveMixedShapeByAspectRatio', () => {
|
describe('resolveMixedShapeByAspectRatio', () => {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { CardShape } from '../../utils/card';
|
|
||||||
import { randomInt } from '../../utils/number';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
import { CardShape } from 'utils/card';
|
||||||
|
import { randomInt } from 'utils/number';
|
||||||
|
|
||||||
const ASPECT_RATIOS = {
|
const ASPECT_RATIOS = {
|
||||||
portrait: (2 / 3),
|
portrait: (2 / 3),
|
||||||
backdrop: (16 / 9),
|
backdrop: (16 / 9),
|
||||||
@@ -20,12 +22,12 @@ export const isUsingLiveTvNaming = (itemType: string | null | undefined): boolea
|
|||||||
* Resolves Card action to display
|
* Resolves Card action to display
|
||||||
* @param opts options to determine the action to return
|
* @param opts options to determine the action to return
|
||||||
*/
|
*/
|
||||||
export const resolveAction = (opts: { defaultAction: string, isFolder: boolean, isPhoto: boolean }): string => {
|
export const resolveAction = (opts: { defaultAction: ItemAction, isFolder: boolean, isPhoto: boolean }): ItemAction => {
|
||||||
if (opts.defaultAction === 'play' && opts.isFolder) {
|
if (opts.defaultAction === ItemAction.Play && opts.isFolder) {
|
||||||
// If this hard-coding is ever removed make sure to test nested photo albums
|
// If this hard-coding is ever removed make sure to test nested photo albums
|
||||||
return 'link';
|
return ItemAction.Link;
|
||||||
} else if (opts.isPhoto) {
|
} else if (opts.isPhoto) {
|
||||||
return 'play';
|
return ItemAction.Play;
|
||||||
} else {
|
} else {
|
||||||
return opts.defaultAction;
|
return opts.defaultAction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import InfoIcon from '@mui/icons-material/Info';
|
import InfoIcon from '@mui/icons-material/Info';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
|
|
||||||
interface InfoIconButtonProps {
|
interface InfoIconButtonProps {
|
||||||
@@ -11,7 +13,7 @@ const InfoIconButton: FC<InfoIconButtonProps> = ({ className }) => {
|
|||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
className={className}
|
className={className}
|
||||||
data-action='link'
|
data-action={ItemAction.Link}
|
||||||
title={globalize.translate('ButtonInfo')}
|
title={globalize.translate('ButtonInfo')}
|
||||||
>
|
>
|
||||||
<InfoIcon />
|
<InfoIcon />
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
|
|
||||||
interface MoreVertIconButtonProps {
|
interface MoreVertIconButtonProps {
|
||||||
@@ -12,7 +14,7 @@ const MoreVertIconButton: FC<MoreVertIconButtonProps> = ({ className, iconClassN
|
|||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
className={className}
|
className={className}
|
||||||
data-action='menu'
|
data-action={ItemAction.Menu}
|
||||||
title={globalize.translate('ButtonMore')}
|
title={globalize.translate('ButtonMore')}
|
||||||
>
|
>
|
||||||
<MoreVertIcon className={iconClassName} />
|
<MoreVertIcon className={iconClassName} />
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
|
|
||||||
interface PlayArrowIconButtonProps {
|
interface PlayArrowIconButtonProps {
|
||||||
className: string;
|
className: string;
|
||||||
action: string;
|
action: ItemAction;
|
||||||
title: string;
|
title: string;
|
||||||
iconClassName?: string;
|
iconClassName?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
|
|
||||||
interface PlaylistAddIconButtonProps {
|
interface PlaylistAddIconButtonProps {
|
||||||
@@ -11,7 +13,7 @@ const PlaylistAddIconButton: FC<PlaylistAddIconButtonProps> = ({ className }) =>
|
|||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
className={className}
|
className={className}
|
||||||
data-action='addtoplaylist'
|
data-action={ItemAction.AddToPlaylist}
|
||||||
title={globalize.translate('AddToPlaylist')}
|
title={globalize.translate('AddToPlaylist')}
|
||||||
>
|
>
|
||||||
<PlaylistAddIcon />
|
<PlaylistAddIcon />
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
|
||||||
interface RightIconButtonsProps {
|
interface RightIconButtonsProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
id: string;
|
id: string;
|
||||||
@@ -12,7 +14,7 @@ const RightIconButtons: FC<RightIconButtonsProps> = ({ className, id, title, ico
|
|||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
className={className}
|
className={className}
|
||||||
data-action='custom'
|
data-action={ItemAction.Custom}
|
||||||
data-customaction={id}
|
data-customaction={id}
|
||||||
title={title}
|
title={title}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import escapeHtml from 'escape-html';
|
import escapeHtml from 'escape-html';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||||
|
|
||||||
import inputManager from '../../scripts/inputManager';
|
import inputManager from '../../scripts/inputManager';
|
||||||
import browser from '../../scripts/browser';
|
import browser from '../../scripts/browser';
|
||||||
import globalize from '../../lib/globalize';
|
import globalize from '../../lib/globalize';
|
||||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
|
||||||
import Events from '../../utils/events.ts';
|
import Events from '../../utils/events.ts';
|
||||||
import scrollHelper from '../../scripts/scrollHelper';
|
import scrollHelper from '../../scripts/scrollHelper';
|
||||||
import serverNotifications from '../../scripts/serverNotifications';
|
import serverNotifications from '../../scripts/serverNotifications';
|
||||||
@@ -15,6 +18,7 @@ import imageLoader from '../images/imageLoader';
|
|||||||
import layoutManager from '../layoutManager';
|
import layoutManager from '../layoutManager';
|
||||||
import itemShortcuts from '../shortcuts';
|
import itemShortcuts from '../shortcuts';
|
||||||
import dom from '../../utils/dom';
|
import dom from '../../utils/dom';
|
||||||
|
|
||||||
import './guide.scss';
|
import './guide.scss';
|
||||||
import './programs.scss';
|
import './programs.scss';
|
||||||
import 'material-design-icons-iconfont';
|
import 'material-design-icons-iconfont';
|
||||||
@@ -26,6 +30,7 @@ import '../../elements/emby-tabs/emby-tabs';
|
|||||||
import '../../elements/emby-scroller/emby-scroller';
|
import '../../elements/emby-scroller/emby-scroller';
|
||||||
import '../../styles/flexstyles.scss';
|
import '../../styles/flexstyles.scss';
|
||||||
import 'webcomponents.js/webcomponents-lite';
|
import 'webcomponents.js/webcomponents-lite';
|
||||||
|
|
||||||
import template from './tvguide.template.html';
|
import template from './tvguide.template.html';
|
||||||
|
|
||||||
function showViewSettings(instance) {
|
function showViewSettings(instance) {
|
||||||
@@ -441,7 +446,7 @@ function Guide(options) {
|
|||||||
|
|
||||||
html += '<div class="' + outerCssClass + '" data-channelid="' + channel.Id + '">';
|
html += '<div class="' + outerCssClass + '" data-channelid="' + channel.Id + '">';
|
||||||
|
|
||||||
const clickAction = layoutManager.tv ? 'link' : 'programdialog';
|
const clickAction = layoutManager.tv ? ItemAction.Link : ItemAction.ProgramDialog;
|
||||||
|
|
||||||
const categories = self.categoryOptions.categories || [];
|
const categories = self.categoryOptions.categories || [];
|
||||||
const displayMovieContent = !categories.length || categories.indexOf('movies') !== -1;
|
const displayMovieContent = !categories.length || categories.indexOf('movies') !== -1;
|
||||||
@@ -607,7 +612,7 @@ function Guide(options) {
|
|||||||
title.push(channel.Name);
|
title.push(channel.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '<button title="' + escapeHtml(title.join(' ')) + '" type="button" class="' + cssClass + '"' + ' data-action="link" data-isfolder="' + channel.IsFolder + '" data-id="' + channel.Id + '" data-serverid="' + channel.ServerId + '" data-type="' + channel.Type + '">';
|
html += `<button title="${escapeHtml(title.join(' '))}" type="button" class="${cssClass}" data-action="${ItemAction.Link}" data-isfolder="${channel.IsFolder}" data-id="${channel.Id}" data-serverid="${channel.ServerId}" data-type="${channel.Type}">`;
|
||||||
|
|
||||||
if (hasChannelImage) {
|
if (hasChannelImage) {
|
||||||
const url = apiClient.getScaledImageUrl(channel.Id, {
|
const url = apiClient.getScaledImageUrl(channel.Id, {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import DragHandleIcon from '@mui/icons-material/DragHandle';
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
import useIndicator from 'components/indicators/useIndicator';
|
import useIndicator from 'components/indicators/useIndicator';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
|
||||||
import PrimaryMediaInfo from '../../mediainfo/PrimaryMediaInfo';
|
import PrimaryMediaInfo from '../../mediainfo/PrimaryMediaInfo';
|
||||||
import ListContentWrapper from './ListContentWrapper';
|
import ListContentWrapper from './ListContentWrapper';
|
||||||
import ListItemBody from './ListItemBody';
|
import ListItemBody from './ListItemBody';
|
||||||
@@ -20,7 +22,7 @@ interface ListContentProps {
|
|||||||
enableOverview?: boolean;
|
enableOverview?: boolean;
|
||||||
enableSideMediaInfo?: boolean;
|
enableSideMediaInfo?: boolean;
|
||||||
clickEntireItem?: boolean;
|
clickEntireItem?: boolean;
|
||||||
action?: string;
|
action?: ItemAction;
|
||||||
isLargeStyle: boolean;
|
isLargeStyle: boolean;
|
||||||
downloadWidth?: number;
|
downloadWidth?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import React, { type FC } from 'react';
|
import React, { type FC } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
|
import Media from 'components/common/Media';
|
||||||
|
import PlayArrowIconButton from 'components/common/PlayArrowIconButton';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import { useApi } from 'hooks/useApi';
|
import { useApi } from 'hooks/useApi';
|
||||||
|
import type { ItemDto } from 'types/base/models/item-dto';
|
||||||
|
import type { ListOptions } from 'types/listOptions';
|
||||||
|
|
||||||
import useIndicator from '../../indicators/useIndicator';
|
import useIndicator from '../../indicators/useIndicator';
|
||||||
import layoutManager from '../../layoutManager';
|
import layoutManager from '../../layoutManager';
|
||||||
import { getDefaultBackgroundClass } from '../../cardbuilder/cardBuilderUtils';
|
import { getDefaultBackgroundClass } from '../../cardbuilder/cardBuilderUtils';
|
||||||
@@ -11,15 +18,10 @@ import {
|
|||||||
getImageUrl
|
getImageUrl
|
||||||
} from './listHelper';
|
} from './listHelper';
|
||||||
|
|
||||||
import Media from 'components/common/Media';
|
|
||||||
import PlayArrowIconButton from 'components/common/PlayArrowIconButton';
|
|
||||||
import type { ItemDto } from 'types/base/models/item-dto';
|
|
||||||
import type { ListOptions } from 'types/listOptions';
|
|
||||||
|
|
||||||
interface ListImageContainerProps {
|
interface ListImageContainerProps {
|
||||||
item: ItemDto;
|
item: ItemDto;
|
||||||
listOptions: ListOptions;
|
listOptions: ListOptions;
|
||||||
action?: string | null;
|
action?: ItemAction | null;
|
||||||
isLargeStyle: boolean;
|
isLargeStyle: boolean;
|
||||||
clickEntireItem?: boolean;
|
clickEntireItem?: boolean;
|
||||||
downloadWidth?: number;
|
downloadWidth?: number;
|
||||||
@@ -55,7 +57,7 @@ const ListImageContainer: FC<ListImageContainerProps> = ({
|
|||||||
|
|
||||||
const playOnImageClick = listOptions.imagePlayButton && !layoutManager.tv;
|
const playOnImageClick = listOptions.imagePlayButton && !layoutManager.tv;
|
||||||
|
|
||||||
const imageAction = playOnImageClick ? 'link' : action;
|
const imageAction = playOnImageClick ? ItemAction.Link : action;
|
||||||
|
|
||||||
const btnCssClass =
|
const btnCssClass =
|
||||||
'paper-icon-button-light listItemImageButton itemAction';
|
'paper-icon-button-light listItemImageButton itemAction';
|
||||||
@@ -85,7 +87,7 @@ const ListImageContainer: FC<ListImageContainerProps> = ({
|
|||||||
<PlayArrowIconButton
|
<PlayArrowIconButton
|
||||||
className={btnCssClass}
|
className={btnCssClass}
|
||||||
action={
|
action={
|
||||||
canResume(playbackPositionTicks) ? 'resume' : 'play'
|
canResume(playbackPositionTicks) ? ItemAction.Resume : ItemAction.Play
|
||||||
}
|
}
|
||||||
title={
|
title={
|
||||||
canResume(playbackPositionTicks) ?
|
canResume(playbackPositionTicks) ?
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ import classNames from 'classnames';
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
import TextLines from 'components/common/textLines/TextLines';
|
import TextLines from 'components/common/textLines/TextLines';
|
||||||
import PrimaryMediaInfo from '../../mediainfo/PrimaryMediaInfo';
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
|
||||||
import type { ItemDto } from 'types/base/models/item-dto';
|
import type { ItemDto } from 'types/base/models/item-dto';
|
||||||
import type { ListOptions } from 'types/listOptions';
|
import type { ListOptions } from 'types/listOptions';
|
||||||
|
|
||||||
|
import PrimaryMediaInfo from '../../mediainfo/PrimaryMediaInfo';
|
||||||
|
|
||||||
interface ListItemBodyProps {
|
interface ListItemBodyProps {
|
||||||
item: ItemDto;
|
item: ItemDto;
|
||||||
listOptions: ListOptions;
|
listOptions: ListOptions;
|
||||||
action?: string | null;
|
action?: ItemAction | null;
|
||||||
isLargeStyle?: boolean;
|
isLargeStyle?: boolean;
|
||||||
clickEntireItem?: boolean;
|
clickEntireItem?: boolean;
|
||||||
enableContentWrapper?: boolean;
|
enableContentWrapper?: boolean;
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ import classNames from 'classnames';
|
|||||||
import React, { type FC, type PropsWithChildren } from 'react';
|
import React, { type FC, type PropsWithChildren } from 'react';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import layoutManager from '../../layoutManager';
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import type { DataAttributes } from 'types/dataAttributes';
|
import type { DataAttributes } from 'types/dataAttributes';
|
||||||
|
|
||||||
|
import layoutManager from '../../layoutManager';
|
||||||
|
|
||||||
interface ListWrapperProps {
|
interface ListWrapperProps {
|
||||||
index: number | undefined;
|
index: number | undefined;
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
action?: string | null;
|
action?: ItemAction | null;
|
||||||
dataAttributes?: DataAttributes;
|
dataAttributes?: DataAttributes;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { getDataAttributes } from 'utils/items';
|
|
||||||
import layoutManager from 'components/layoutManager';
|
import layoutManager from 'components/layoutManager';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
import { getDataAttributes } from 'utils/items';
|
||||||
|
|
||||||
import type { ItemDto } from 'types/base/models/item-dto';
|
import type { ItemDto } from 'types/base/models/item-dto';
|
||||||
import type { ListOptions } from 'types/listOptions';
|
import type { ListOptions } from 'types/listOptions';
|
||||||
@@ -11,7 +13,7 @@ interface UseListProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function useList({ item, listOptions }: UseListProps) {
|
function useList({ item, listOptions }: UseListProps) {
|
||||||
const action = listOptions.action ?? 'link';
|
const action = listOptions.action ?? ItemAction.Link;
|
||||||
const isLargeStyle = listOptions.imageSize === 'large';
|
const isLargeStyle = listOptions.imageSize === 'large';
|
||||||
const enableOverview = listOptions.enableOverview;
|
const enableOverview = listOptions.enableOverview;
|
||||||
const clickEntireItem = !!layoutManager.tv;
|
const clickEntireItem = !!layoutManager.tv;
|
||||||
|
|||||||
@@ -4,7 +4,13 @@
|
|||||||
* @module components/listview/listview
|
* @module components/listview/listview
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
import escapeHtml from 'escape-html';
|
import escapeHtml from 'escape-html';
|
||||||
|
import markdownIt from 'markdown-it';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
|
||||||
|
import { getDefaultBackgroundClass } from '../cardbuilder/cardBuilderUtils';
|
||||||
import itemHelper from '../itemHelper';
|
import itemHelper from '../itemHelper';
|
||||||
import mediaInfo from '../mediainfo/mediainfo';
|
import mediaInfo from '../mediainfo/mediainfo';
|
||||||
import indicators from '../indicators/indicators';
|
import indicators from '../indicators/indicators';
|
||||||
@@ -13,12 +19,10 @@ import globalize from '../../lib/globalize';
|
|||||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||||
import datetime from '../../scripts/datetime';
|
import datetime from '../../scripts/datetime';
|
||||||
import cardBuilder from '../cardbuilder/cardBuilder';
|
import cardBuilder from '../cardbuilder/cardBuilder';
|
||||||
|
|
||||||
import './listview.scss';
|
import './listview.scss';
|
||||||
import '../../elements/emby-ratingbutton/emby-ratingbutton';
|
import '../../elements/emby-ratingbutton/emby-ratingbutton';
|
||||||
import '../../elements/emby-playstatebutton/emby-playstatebutton';
|
import '../../elements/emby-playstatebutton/emby-playstatebutton';
|
||||||
import { getDefaultBackgroundClass } from '../cardbuilder/cardBuilderUtils';
|
|
||||||
import markdownIt from 'markdown-it';
|
|
||||||
import DOMPurify from 'dompurify';
|
|
||||||
|
|
||||||
function getIndex(item, options) {
|
function getIndex(item, options) {
|
||||||
if (options.index === 'disc') {
|
if (options.index === 'disc') {
|
||||||
@@ -165,7 +169,7 @@ function getRightButtonsHtml(options) {
|
|||||||
for (let i = 0, length = options.rightButtons.length; i < length; i++) {
|
for (let i = 0, length = options.rightButtons.length; i < length; i++) {
|
||||||
const button = options.rightButtons[i];
|
const button = options.rightButtons[i];
|
||||||
|
|
||||||
html += `<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="custom" data-customaction="${button.id}" title="${button.title}"><span class="material-icons ${button.icon}" aria-hidden="true"></span></button>`;
|
html += `<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="${ItemAction.Custom}" data-customaction="${button.id}" title="${button.title}"><span class="material-icons ${button.icon}" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
@@ -175,7 +179,7 @@ export function getListViewHtml(options) {
|
|||||||
const items = options.items;
|
const items = options.items;
|
||||||
|
|
||||||
let groupTitle = '';
|
let groupTitle = '';
|
||||||
const action = options.action || 'link';
|
const action = options.action || ItemAction.Link;
|
||||||
|
|
||||||
const isLargeStyle = options.imageSize === 'large';
|
const isLargeStyle = options.imageSize === 'large';
|
||||||
const enableOverview = options.enableOverview;
|
const enableOverview = options.enableOverview;
|
||||||
@@ -277,7 +281,7 @@ export function getListViewHtml(options) {
|
|||||||
imageClass += ' itemAction';
|
imageClass += ' itemAction';
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageAction = playOnImageClick ? 'link' : action;
|
const imageAction = playOnImageClick ? ItemAction.Link : action;
|
||||||
|
|
||||||
if (imgUrl) {
|
if (imgUrl) {
|
||||||
html += '<div data-action="' + imageAction + '" class="' + imageClass + ' lazy" data-src="' + imgUrl + '" item-icon>';
|
html += '<div data-action="' + imageAction + '" class="' + imageClass + ' lazy" data-src="' + imgUrl + '" item-icon>';
|
||||||
@@ -298,7 +302,7 @@ export function getListViewHtml(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (playOnImageClick) {
|
if (playOnImageClick) {
|
||||||
html += '<button is="paper-icon-button-light" class="listItemImageButton itemAction" data-action="resume"><span class="material-icons listItemImageButton-icon play_arrow" aria-hidden="true"></span></button>';
|
html += `<button is="paper-icon-button-light" class="listItemImageButton itemAction" data-action="${ItemAction.Resume}"><span class="material-icons listItemImageButton-icon play_arrow" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const progressHtml = indicators.getProgressBarHtml(item, {
|
const progressHtml = indicators.getProgressBarHtml(item, {
|
||||||
@@ -449,11 +453,11 @@ export function getListViewHtml(options) {
|
|||||||
|
|
||||||
if (!clickEntireItem) {
|
if (!clickEntireItem) {
|
||||||
if (options.addToListButton) {
|
if (options.addToListButton) {
|
||||||
html += '<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="addtoplaylist"><span class="material-icons playlist_add" aria-hidden="true"></span></button>';
|
html += `<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="${ItemAction.AddToPlaylist}"><span class="material-icons playlist_add" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.infoButton) {
|
if (options.infoButton) {
|
||||||
html += '<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="link"><span class="material-icons info_outline" aria-hidden="true"></span></button>';
|
html += `<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="${ItemAction.Link}"><span class="material-icons info_outline" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.rightButtons) {
|
if (options.rightButtons) {
|
||||||
@@ -474,7 +478,7 @@ export function getListViewHtml(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.moreButton !== false) {
|
if (options.moreButton !== false) {
|
||||||
html += '<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="menu"><span class="material-icons more_vert" aria-hidden="true"></span></button>';
|
html += `<button is="paper-icon-button-light" class="listItemButton itemAction" data-action="${ItemAction.Menu}"><span class="material-icons more_vert" aria-hidden="true"></span></button>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import escapeHtml from 'escape-html';
|
|||||||
import { getImageUrl } from 'apps/stable/features/playback/utils/image';
|
import { getImageUrl } from 'apps/stable/features/playback/utils/image';
|
||||||
import { getItemTextLines } from 'apps/stable/features/playback/utils/itemText';
|
import { getItemTextLines } from 'apps/stable/features/playback/utils/itemText';
|
||||||
import { AppFeature } from 'constants/appFeature';
|
import { AppFeature } from 'constants/appFeature';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
|
||||||
import datetime from '../../scripts/datetime';
|
import datetime from '../../scripts/datetime';
|
||||||
import { clearBackdrop, setBackdrops } from '../backdrop/backdrop';
|
import { clearBackdrop, setBackdrops } from '../backdrop/backdrop';
|
||||||
@@ -16,6 +17,9 @@ import { ServerConnections } from 'lib/jellyfin-apiclient';
|
|||||||
import layoutManager from '../layoutManager';
|
import layoutManager from '../layoutManager';
|
||||||
import * as userSettings from '../../scripts/settings/userSettings';
|
import * as userSettings from '../../scripts/settings/userSettings';
|
||||||
import itemContextMenu from '../itemContextMenu';
|
import itemContextMenu from '../itemContextMenu';
|
||||||
|
import toast from '../toast/toast';
|
||||||
|
import { appRouter } from '../router/appRouter';
|
||||||
|
import { getDefaultBackgroundClass } from '../cardbuilder/cardBuilderUtils';
|
||||||
|
|
||||||
import '../cardbuilder/card.scss';
|
import '../cardbuilder/card.scss';
|
||||||
import '../../elements/emby-button/emby-button';
|
import '../../elements/emby-button/emby-button';
|
||||||
@@ -24,9 +28,6 @@ import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
|||||||
import './remotecontrol.scss';
|
import './remotecontrol.scss';
|
||||||
import '../../elements/emby-ratingbutton/emby-ratingbutton';
|
import '../../elements/emby-ratingbutton/emby-ratingbutton';
|
||||||
import '../../elements/emby-slider/emby-slider';
|
import '../../elements/emby-slider/emby-slider';
|
||||||
import toast from '../toast/toast';
|
|
||||||
import { appRouter } from '../router/appRouter';
|
|
||||||
import { getDefaultBackgroundClass } from '../cardbuilder/cardBuilderUtils';
|
|
||||||
|
|
||||||
let showMuteButton = true;
|
let showMuteButton = true;
|
||||||
let showVolumeSlider = true;
|
let showVolumeSlider = true;
|
||||||
@@ -208,7 +209,7 @@ function setImageUrl(context, state, url) {
|
|||||||
context.querySelector('.nowPlayingPageImage').classList.toggle('nowPlayingPageImageAudio', item.Type === 'Audio');
|
context.querySelector('.nowPlayingPageImage').classList.toggle('nowPlayingPageImageAudio', item.Type === 'Audio');
|
||||||
context.querySelector('.nowPlayingPageImage').classList.toggle('nowPlayingPageImagePoster', item.Type !== 'Audio');
|
context.querySelector('.nowPlayingPageImage').classList.toggle('nowPlayingPageImagePoster', item.Type !== 'Audio');
|
||||||
} else {
|
} else {
|
||||||
imgContainer.innerHTML = '<div class="nowPlayingPageImageContainerNoAlbum"><button data-action="link" class="cardImageContainer coveredImage ' + getDefaultBackgroundClass(item.Name) + ' cardContent cardContent-shadow itemAction"><span class="cardImageIcon material-icons album" aria-hidden="true"></span></button></div>';
|
imgContainer.innerHTML = `<div class="nowPlayingPageImageContainerNoAlbum"><button data-action="${ItemAction.Link}" class="cardImageContainer coveredImage ${getDefaultBackgroundClass(item.Name)} cardContent cardContent-shadow itemAction"><span class="cardImageIcon material-icons album" aria-hidden="true"></span></button></div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-22
@@ -1,19 +1,20 @@
|
|||||||
/**
|
/**
|
||||||
* Module shortcuts.
|
* "Shortcut" action handlers for BaseItems.
|
||||||
* @module components/shortcuts
|
|
||||||
*/
|
*/
|
||||||
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
|
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||||
|
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||||
|
|
||||||
import { playbackManager } from './playback/playbackmanager';
|
import { playbackManager } from './playback/playbackmanager';
|
||||||
import inputManager from '../scripts/inputManager';
|
import inputManager from '../scripts/inputManager';
|
||||||
import { appRouter } from './router/appRouter';
|
import { appRouter } from './router/appRouter';
|
||||||
import globalize from '../lib/globalize';
|
import globalize from '../lib/globalize';
|
||||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
|
||||||
import dom from '../utils/dom';
|
import dom from '../utils/dom';
|
||||||
import recordingHelper from './recordingcreator/recordinghelper';
|
import recordingHelper from './recordingcreator/recordinghelper';
|
||||||
import toast from './toast/toast';
|
import toast from './toast/toast';
|
||||||
import * as userSettings from '../scripts/settings/userSettings';
|
import * as userSettings from '../scripts/settings/userSettings';
|
||||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
|
||||||
|
|
||||||
function playAllFromHere(card, serverId, queue) {
|
function playAllFromHere(card, serverId, queue) {
|
||||||
const parent = card.parentNode;
|
const parent = card.parentNode;
|
||||||
@@ -231,23 +232,28 @@ function executeAction(card, target, action) {
|
|||||||
|
|
||||||
const playableItemId = type === 'Program' ? item.ChannelId : item.Id;
|
const playableItemId = type === 'Program' ? item.ChannelId : item.Id;
|
||||||
|
|
||||||
if (item.MediaType === 'Photo' && action === 'link') {
|
if (item.MediaType === 'Photo' && action === ItemAction.Link) {
|
||||||
action = 'play';
|
action = ItemAction.Play;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'link') {
|
switch (action) {
|
||||||
|
case ItemAction.Link:
|
||||||
appRouter.showItem(item, {
|
appRouter.showItem(item, {
|
||||||
context: card.getAttribute('data-context'),
|
context: card.getAttribute('data-context'),
|
||||||
parentId: card.getAttribute('data-parentid')
|
parentId: card.getAttribute('data-parentid')
|
||||||
});
|
});
|
||||||
} else if (action === 'programdialog') {
|
break;
|
||||||
|
case ItemAction.ProgramDialog:
|
||||||
showProgramDialog(item);
|
showProgramDialog(item);
|
||||||
} else if (action === 'instantmix') {
|
break;
|
||||||
|
case ItemAction.InstantMix:
|
||||||
playbackManager.instantMix({
|
playbackManager.instantMix({
|
||||||
Id: playableItemId,
|
Id: playableItemId,
|
||||||
ServerId: serverId
|
ServerId: serverId
|
||||||
});
|
});
|
||||||
} else if (action === 'play' || action === 'resume') {
|
break;
|
||||||
|
case ItemAction.Play:
|
||||||
|
case ItemAction.Resume: {
|
||||||
const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0', 10);
|
const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0', 10);
|
||||||
const sortValues = userSettings.getSortValuesLegacy(sortParentId, 'SortName');
|
const sortValues = userSettings.getSortValuesLegacy(sortParentId, 'SortName');
|
||||||
|
|
||||||
@@ -264,7 +270,9 @@ function executeAction(card, target, action) {
|
|||||||
} else {
|
} else {
|
||||||
console.warn('Unable to play item', item);
|
console.warn('Unable to play item', item);
|
||||||
}
|
}
|
||||||
} else if (action === 'queue') {
|
break;
|
||||||
|
}
|
||||||
|
case ItemAction.Queue:
|
||||||
if (playbackManager.isPlaying()) {
|
if (playbackManager.isPlaying()) {
|
||||||
playbackManager.queue({
|
playbackManager.queue({
|
||||||
ids: [playableItemId],
|
ids: [playableItemId],
|
||||||
@@ -277,15 +285,20 @@ function executeAction(card, target, action) {
|
|||||||
serverId: serverId
|
serverId: serverId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (action === 'playallfromhere') {
|
break;
|
||||||
|
case ItemAction.PlayAllFromHere:
|
||||||
playAllFromHere(card, serverId);
|
playAllFromHere(card, serverId);
|
||||||
} else if (action === 'queueallfromhere') {
|
break;
|
||||||
|
case ItemAction.QueueAllFromHere:
|
||||||
playAllFromHere(card, serverId, true);
|
playAllFromHere(card, serverId, true);
|
||||||
} else if (action === 'setplaylistindex') {
|
break;
|
||||||
|
case ItemAction.SetPlaylistIndex:
|
||||||
playbackManager.setCurrentPlaylistItem(card.getAttribute('data-playlistitemid'));
|
playbackManager.setCurrentPlaylistItem(card.getAttribute('data-playlistitemid'));
|
||||||
} else if (action === 'record') {
|
break;
|
||||||
|
case ItemAction.Record:
|
||||||
onRecordCommand(serverId, id, type, card.getAttribute('data-timerid'), card.getAttribute('data-seriestimerid'));
|
onRecordCommand(serverId, id, type, card.getAttribute('data-timerid'), card.getAttribute('data-seriestimerid'));
|
||||||
} else if (action === 'menu') {
|
break;
|
||||||
|
case ItemAction.Menu: {
|
||||||
const options = target.getAttribute('data-playoptions') === 'false' ?
|
const options = target.getAttribute('data-playoptions') === 'false' ?
|
||||||
{
|
{
|
||||||
shuffle: false,
|
shuffle: false,
|
||||||
@@ -300,17 +313,23 @@ function executeAction(card, target, action) {
|
|||||||
options.positionTo = target;
|
options.positionTo = target;
|
||||||
|
|
||||||
showContextMenu(card, options);
|
showContextMenu(card, options);
|
||||||
} else if (action === 'playmenu') {
|
break;
|
||||||
|
}
|
||||||
|
case ItemAction.PlayMenu:
|
||||||
showPlayMenu(card, target);
|
showPlayMenu(card, target);
|
||||||
} else if (action === 'edit') {
|
break;
|
||||||
|
case ItemAction.Edit:
|
||||||
getItem(target).then(itemToEdit => {
|
getItem(target).then(itemToEdit => {
|
||||||
editItem(itemToEdit, serverId);
|
editItem(itemToEdit, serverId);
|
||||||
});
|
});
|
||||||
} else if (action === 'playtrailer') {
|
break;
|
||||||
|
case ItemAction.PlayTrailer:
|
||||||
getItem(target).then(playTrailer);
|
getItem(target).then(playTrailer);
|
||||||
} else if (action === 'addtoplaylist') {
|
break;
|
||||||
|
case ItemAction.AddToPlaylist:
|
||||||
getItem(target).then(addToPlaylist);
|
getItem(target).then(addToPlaylist);
|
||||||
} else if (action === 'custom') {
|
break;
|
||||||
|
case ItemAction.Custom: {
|
||||||
const customAction = target.getAttribute('data-customaction');
|
const customAction = target.getAttribute('data-customaction');
|
||||||
|
|
||||||
card.dispatchEvent(new CustomEvent(`action-${customAction}`, {
|
card.dispatchEvent(new CustomEvent(`action-${customAction}`, {
|
||||||
@@ -321,6 +340,7 @@ function executeAction(card, target, action) {
|
|||||||
bubbles: true
|
bubbles: true
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToPlaylist(item) {
|
function addToPlaylist(item) {
|
||||||
@@ -390,7 +410,7 @@ export function onClick(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action && action !== 'none') {
|
if (action && action !== ItemAction.None) {
|
||||||
executeAction(card, actionElement, action);
|
executeAction(card, actionElement, action);
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/** Actions that can be performed on a BaseItem. */
|
||||||
|
export enum ItemAction {
|
||||||
|
/** Add the Item to a playlist. */
|
||||||
|
AddToPlaylist = 'addtoplaylist',
|
||||||
|
/** Trigger a custom action via an Event. */
|
||||||
|
Custom = 'custom',
|
||||||
|
/** Open an editor for the Item. */
|
||||||
|
Edit = 'edit',
|
||||||
|
/** Create an instant mix based on the Item. */
|
||||||
|
InstantMix = 'instantmix',
|
||||||
|
/** Open the details view for the Item. */
|
||||||
|
Link = 'link',
|
||||||
|
/** Open the context menu for the Item. */
|
||||||
|
Menu = 'menu',
|
||||||
|
/** Perform no action. Used to prevent a parent element's action being triggered. */
|
||||||
|
None = 'none',
|
||||||
|
/** Play the Item. */
|
||||||
|
Play = 'play',
|
||||||
|
/** Queue the Item and all subsequent Items and start playback. */
|
||||||
|
PlayAllFromHere = 'playallfromhere',
|
||||||
|
/** Open the play menu for the Item. */
|
||||||
|
PlayMenu = 'playmenu',
|
||||||
|
/** Play the trailer for the Item. */
|
||||||
|
PlayTrailer = 'playtrailer',
|
||||||
|
/** Open the program dialog for the Item. */
|
||||||
|
ProgramDialog = 'programdialog',
|
||||||
|
/** Queue the Item. */
|
||||||
|
Queue = 'queue',
|
||||||
|
/** Queue the Item and all subsequent Items. */
|
||||||
|
QueueAllFromHere = 'queueallfromhere',
|
||||||
|
/** Record the Item. */
|
||||||
|
Record = 'record',
|
||||||
|
/** Resume playback of the Item. */
|
||||||
|
Resume = 'resume',
|
||||||
|
/** Set this Item as the Item to be currently played from a playlist. */
|
||||||
|
SetPlaylistIndex = 'setplaylistindex'
|
||||||
|
};
|
||||||
@@ -22,6 +22,7 @@ import { playbackManager } from 'components/playback/playbackmanager';
|
|||||||
import { appRouter } from 'components/router/appRouter';
|
import { appRouter } from 'components/router/appRouter';
|
||||||
import itemShortcuts from 'components/shortcuts';
|
import itemShortcuts from 'components/shortcuts';
|
||||||
import { AppFeature } from 'constants/appFeature';
|
import { AppFeature } from 'constants/appFeature';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||||
import browser from 'scripts/browser';
|
import browser from 'scripts/browser';
|
||||||
@@ -434,19 +435,19 @@ function renderName(item, container, context) {
|
|||||||
parentNameHtml.push(getArtistLinksHtml(item.ArtistItems, item.ServerId, context));
|
parentNameHtml.push(getArtistLinksHtml(item.ArtistItems, item.ServerId, context));
|
||||||
parentNameLast = true;
|
parentNameLast = true;
|
||||||
} else if (item.SeriesName && item.Type === 'Episode') {
|
} else if (item.SeriesName && item.Type === 'Episode') {
|
||||||
parentNameHtml.push(`<a style="color:inherit;" class="button-link itemAction" is="emby-linkbutton" href="#" data-action="link" data-id="${item.SeriesId}" data-serverid="${item.ServerId}" data-type="Series" data-isfolder="true">${escapeHtml(item.SeriesName)}</a>`);
|
parentNameHtml.push(`<a style="color:inherit;" class="button-link itemAction" is="emby-linkbutton" href="#" data-action="${ItemAction.Link}" data-id="${item.SeriesId}" data-serverid="${item.ServerId}" data-type="Series" data-isfolder="true">${escapeHtml(item.SeriesName)}</a>`);
|
||||||
} else if (item.IsSeries || item.EpisodeTitle) {
|
} else if (item.IsSeries || item.EpisodeTitle) {
|
||||||
parentNameHtml.push(escapeHtml(item.Name));
|
parentNameHtml.push(escapeHtml(item.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.SeriesName && item.Type === 'Season') {
|
if (item.SeriesName && item.Type === 'Season') {
|
||||||
parentNameHtml.push(`<a style="color:inherit;" class="button-link itemAction" is="emby-linkbutton" href="#" data-action="link" data-id="${item.SeriesId}" data-serverid="${item.ServerId}" data-type="Series" data-isfolder="true">${escapeHtml(item.SeriesName)}</a>`);
|
parentNameHtml.push(`<a style="color:inherit;" class="button-link itemAction" is="emby-linkbutton" href="#" data-action="${ItemAction.Link}" data-id="${item.SeriesId}" data-serverid="${item.ServerId}" data-type="Series" data-isfolder="true">${escapeHtml(item.SeriesName)}</a>`);
|
||||||
} else if (item.ParentIndexNumber != null && item.Type === 'Episode') {
|
} else if (item.ParentIndexNumber != null && item.Type === 'Episode') {
|
||||||
parentNameHtml.push(`<a style="color:inherit;" class="button-link itemAction" is="emby-linkbutton" href="#" data-action="link" data-id="${item.SeasonId}" data-serverid="${item.ServerId}" data-type="Season" data-isfolder="true">${escapeHtml(item.SeasonName)}</a>`);
|
parentNameHtml.push(`<a style="color:inherit;" class="button-link itemAction" is="emby-linkbutton" href="#" data-action="${ItemAction.Link}" data-id="${item.SeasonId}" data-serverid="${item.ServerId}" data-type="Season" data-isfolder="true">${escapeHtml(item.SeasonName)}</a>`);
|
||||||
} else if (item.ParentIndexNumber != null && item.IsSeries) {
|
} else if (item.ParentIndexNumber != null && item.IsSeries) {
|
||||||
parentNameHtml.push(escapeHtml(item.SeasonName || 'S' + item.ParentIndexNumber));
|
parentNameHtml.push(escapeHtml(item.SeasonName || 'S' + item.ParentIndexNumber));
|
||||||
} else if (item.Album && item.AlbumId && (item.Type === 'MusicVideo' || item.Type === 'Audio')) {
|
} else if (item.Album && item.AlbumId && (item.Type === 'MusicVideo' || item.Type === 'Audio')) {
|
||||||
parentNameHtml.push(`<a style="color:inherit;" class="button-link itemAction" is="emby-linkbutton" href="#" data-action="link" data-id="${item.AlbumId}" data-serverid="${item.ServerId}" data-type="MusicAlbum" data-isfolder="true">${escapeHtml(item.Album)}</a>`);
|
parentNameHtml.push(`<a style="color:inherit;" class="button-link itemAction" is="emby-linkbutton" href="#" data-action="${ItemAction.Link}" data-id="${item.AlbumId}" data-serverid="${item.ServerId}" data-type="MusicAlbum" data-isfolder="true">${escapeHtml(item.Album)}</a>`);
|
||||||
} else if (item.Album) {
|
} else if (item.Album) {
|
||||||
parentNameHtml.push(escapeHtml(item.Album));
|
parentNameHtml.push(escapeHtml(item.Album));
|
||||||
}
|
}
|
||||||
@@ -1982,7 +1983,7 @@ export default function (view, params) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
playItem(item, item.UserData && mode === 'resume' ? item.UserData.PlaybackPositionTicks : 0);
|
playItem(item, item.UserData && mode === ItemAction.Resume ? item.UserData.PlaybackPositionTicks : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPlayClick() {
|
function onPlayClick() {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||||||
import React, { type FC, useCallback } from 'react';
|
import React, { type FC, useCallback } from 'react';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import CheckIcon from '@mui/icons-material/Check';
|
import CheckIcon from '@mui/icons-material/Check';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
import { useTogglePlayedMutation } from 'hooks/useFetchItems';
|
import { useTogglePlayedMutation } from 'hooks/useFetchItems';
|
||||||
|
|
||||||
@@ -59,23 +59,17 @@ const PlayedButton: FC<PlayedButtonProps> = ({
|
|||||||
}
|
}
|
||||||
}, [itemId, togglePlayedMutation, isPlayed, queryClient, queryKey]);
|
}, [itemId, togglePlayedMutation, isPlayed, queryClient, queryKey]);
|
||||||
|
|
||||||
const btnClass = classNames(
|
|
||||||
className,
|
|
||||||
{ 'playstatebutton-played': isPlayed }
|
|
||||||
);
|
|
||||||
|
|
||||||
const iconClass = classNames(
|
|
||||||
{ 'playstatebutton-icon-played': isPlayed }
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
data-action='none'
|
data-action={ItemAction.None}
|
||||||
title={getTitle()}
|
title={getTitle()}
|
||||||
className={btnClass}
|
className={className}
|
||||||
size='small'
|
size='small'
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<CheckIcon className={iconClass} />
|
<CheckIcon
|
||||||
|
color={isPlayed ? 'error' : undefined}
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import FavoriteIcon from '@mui/icons-material/Favorite';
|
import FavoriteIcon from '@mui/icons-material/Favorite';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import { useToggleFavoriteMutation } from 'hooks/useFetchItems';
|
import { useToggleFavoriteMutation } from 'hooks/useFetchItems';
|
||||||
import globalize from 'lib/globalize';
|
import globalize from 'lib/globalize';
|
||||||
|
|
||||||
@@ -45,24 +45,17 @@ const FavoriteButton: FC<FavoriteButtonProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isFavorite, itemId, queryClient, queryKey, toggleFavoriteMutation]);
|
}, [isFavorite, itemId, queryClient, queryKey, toggleFavoriteMutation]);
|
||||||
|
|
||||||
const btnClass = classNames(
|
|
||||||
className,
|
|
||||||
{ 'ratingbutton-withrating': isFavorite }
|
|
||||||
);
|
|
||||||
|
|
||||||
const iconClass = classNames(
|
|
||||||
{ 'ratingbutton-icon-withrating': isFavorite }
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
data-action='none'
|
data-action={ItemAction.None}
|
||||||
|
className={className}
|
||||||
title={isFavorite ? globalize.translate('Favorite') : globalize.translate('AddToFavorites')}
|
title={isFavorite ? globalize.translate('Favorite') : globalize.translate('AddToFavorites')}
|
||||||
className={btnClass}
|
|
||||||
size='small'
|
size='small'
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<FavoriteIcon className={iconClass} />
|
<FavoriteIcon
|
||||||
|
color={isFavorite ? 'error' : undefined}
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import type { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-
|
|||||||
import type { UserItemDataDto } from '@jellyfin/sdk/lib/generated-client/models/user-item-data-dto';
|
import type { UserItemDataDto } from '@jellyfin/sdk/lib/generated-client/models/user-item-data-dto';
|
||||||
import type { BaseItemDtoImageBlurHashes } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto-image-blur-hashes';
|
import type { BaseItemDtoImageBlurHashes } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto-image-blur-hashes';
|
||||||
import type { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
import type { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import { CardShape } from 'utils/card';
|
import { CardShape } from 'utils/card';
|
||||||
|
|
||||||
import type { NullableString } from './base/common/shared/types';
|
import type { NullableString } from './base/common/shared/types';
|
||||||
import type { ItemDto } from './base/models/item-dto';
|
import type { ItemDto } from './base/models/item-dto';
|
||||||
import type { ParentId } from './library';
|
import type { ParentId } from './library';
|
||||||
@@ -44,7 +47,7 @@ export interface CardOptions {
|
|||||||
showChildCountIndicator?: boolean;
|
showChildCountIndicator?: boolean;
|
||||||
lines?: number;
|
lines?: number;
|
||||||
context?: CollectionType;
|
context?: CollectionType;
|
||||||
action?: string | null;
|
action?: ItemAction | null;
|
||||||
indexBy?: string;
|
indexBy?: string;
|
||||||
parentId?: ParentId;
|
parentId?: ParentId;
|
||||||
showMenu?: boolean;
|
showMenu?: boolean;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import type { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
import type { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||||
import type { UserItemDataDto } from '@jellyfin/sdk/lib/generated-client/models/user-item-data-dto';
|
import type { UserItemDataDto } from '@jellyfin/sdk/lib/generated-client/models/user-item-data-dto';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
|
||||||
import type { NullableBoolean, NullableNumber, NullableString } from './base/common/shared/types';
|
import type { NullableBoolean, NullableNumber, NullableString } from './base/common/shared/types';
|
||||||
|
|
||||||
export type AttributesOpts = {
|
export type AttributesOpts = {
|
||||||
@@ -8,7 +11,7 @@ export type AttributesOpts = {
|
|||||||
collectionId?: NullableString,
|
collectionId?: NullableString,
|
||||||
playlistId?: NullableString,
|
playlistId?: NullableString,
|
||||||
prefix?: NullableString,
|
prefix?: NullableString,
|
||||||
action?: NullableString,
|
action?: ItemAction | null,
|
||||||
itemServerId?: NullableString,
|
itemServerId?: NullableString,
|
||||||
itemId?: NullableString,
|
itemId?: NullableString,
|
||||||
itemTimerId?: NullableString,
|
itemTimerId?: NullableString,
|
||||||
@@ -43,7 +46,7 @@ export type DataAttributes = {
|
|||||||
'data-startdate'?: NullableString;
|
'data-startdate'?: NullableString;
|
||||||
'data-enddate'?: NullableString;
|
'data-enddate'?: NullableString;
|
||||||
'data-prefix'?: NullableString;
|
'data-prefix'?: NullableString;
|
||||||
'data-action'?: NullableString;
|
'data-action'?: ItemAction | null;
|
||||||
'data-positionticks'?: NullableNumber;
|
'data-positionticks'?: NullableNumber;
|
||||||
'data-isfolder'?: NullableBoolean;
|
'data-isfolder'?: NullableBoolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import type { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
import type { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||||
import type { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
import type { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
||||||
import type { ItemDto } from './base/models/item-dto';
|
|
||||||
import type { TextLineOpts } from 'components/common/textLines/types';
|
import type { TextLineOpts } from 'components/common/textLines/types';
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
|
|
||||||
|
import type { ItemDto } from './base/models/item-dto';
|
||||||
|
|
||||||
export interface ListOptions extends TextLineOpts {
|
export interface ListOptions extends TextLineOpts {
|
||||||
items?: ItemDto[] | null;
|
items?: ItemDto[] | null;
|
||||||
index?: string;
|
index?: string;
|
||||||
showIndex?: boolean;
|
showIndex?: boolean;
|
||||||
action?: string | null;
|
action?: ItemAction | null;
|
||||||
imageSize?: string;
|
imageSize?: string;
|
||||||
enableOverview?: boolean;
|
enableOverview?: boolean;
|
||||||
enableSideMediaInfo?: boolean;
|
enableSideMediaInfo?: boolean;
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import { ItemFilter } from '@jellyfin/sdk/lib/generated-client/models/item-filte
|
|||||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||||
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
||||||
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
||||||
|
|
||||||
|
import { ItemAction } from 'constants/itemAction';
|
||||||
import * as userSettings from 'scripts/settings/userSettings';
|
import * as userSettings from 'scripts/settings/userSettings';
|
||||||
import { CardShape } from 'utils/card';
|
|
||||||
import { type Section, SectionType, SectionApiMethod } from 'types/sections';
|
import { type Section, SectionType, SectionApiMethod } from 'types/sections';
|
||||||
|
import { CardShape } from 'utils/card';
|
||||||
|
|
||||||
export const getSuggestionSections = (): Section[] => {
|
export const getSuggestionSections = (): Section[] => {
|
||||||
const parametersOptions = {
|
const parametersOptions = {
|
||||||
@@ -130,7 +132,7 @@ export const getSuggestionSections = (): Section[] => {
|
|||||||
showUnplayedIndicator: false,
|
showUnplayedIndicator: false,
|
||||||
shape: CardShape.SquareOverflow,
|
shape: CardShape.SquareOverflow,
|
||||||
showParentTitle: true,
|
showParentTitle: true,
|
||||||
action: 'instantmix',
|
action: ItemAction.InstantMix,
|
||||||
overlayMoreButton: true,
|
overlayMoreButton: true,
|
||||||
coverImage: true
|
coverImage: true
|
||||||
}
|
}
|
||||||
@@ -149,7 +151,7 @@ export const getSuggestionSections = (): Section[] => {
|
|||||||
showUnplayedIndicator: false,
|
showUnplayedIndicator: false,
|
||||||
shape: CardShape.SquareOverflow,
|
shape: CardShape.SquareOverflow,
|
||||||
showParentTitle: true,
|
showParentTitle: true,
|
||||||
action: 'instantmix',
|
action: ItemAction.InstantMix,
|
||||||
overlayMoreButton: true,
|
overlayMoreButton: true,
|
||||||
coverImage: true
|
coverImage: true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user