Skip to content

Commit

Permalink
Refactored theme media playback settings menus based on github discus…
Browse files Browse the repository at this point in the history
…sion #5958

The Theme Media and Backdrop settings has been moved to separate popover component in the experimental layout. For the classic layout the settings were moved to a separate "Background Playback" settings menu.

Consolidated the widgets to select the Sort By/ Sort Order options for playing theme media to be one a single row with a \<select> (dropdown) widget providing the Sort by options followed by an \<IconButton> with arrow icons which indicates the Sort Order direction.
  • Loading branch information
ItsAllAboutTheCode committed Oct 26, 2024
1 parent 87f1e20 commit a62fbbc
Show file tree
Hide file tree
Showing 14 changed files with 653 additions and 133 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
.backgroundPlaybackDialog {
display: inherit;
margin-bottom: 0;
align-items: center;
position: relative;
min-width: 300px;
}

.backgroundPlaybackPopover {
display: inherit;
margin-bottom: 0;
align-items: center;
position: relative;
min-width: 300px;
}

.backgroundPlaybackStack {
position: relative;
min-width: 300px;
}

#background-playback-settings-header {
margin-bottom: 1em;
}

#background-playback-settings-play-theme-media-label {
margin-left: 9px;
margin-top: 0.5em;
position: relative;
max-width: calc(100% - 24px);
}

#background-playback-sort-by-select-stack {
margin-left: 9px;
margin-bottom: 1.5em;
}

#background-playback-sort-by-select-container {
display: inline-flex;
margin-bottom: 0;
align-items: center;
flex-direction: column;
position: relative;
}


#background-playback-sort-by-select-dropdown {
min-width: 100px;
}

#background-playback-sort-order-icon-button {
position: relative;
display: inline-block;
box-sizing: border-box;
margin: 0;
text-align: center;
font-size: inherit;
font-family: inherit;
color: inherit;

/* These are getting an outline in opera tv browsers, which run chrome 30 */
outline: none !important;
outline-width: 0;
user-select: none;
cursor: pointer;
z-index: 0;
vertical-align: middle;
border: 0;
border-radius: 0.2em;
font-weight: 600;

/* Disable webkit tap highlighting */
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
text-decoration: none;

/* Not crazy about this but it normalizes heights between anchors and buttons */
line-height: 1.35;
transform-origin: center;
transition: 0.2s;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import Checkbox from "@mui/material/Checkbox";
import FormControl from "@mui/material/FormControl";
import FormControlLabel from "@mui/material/FormControlLabel";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import React, { useCallback } from "react";

import globalize from "lib/globalize";

import type { DisplaySettingsValues } from "../types/displaySettingsValues";

import { ItemSortBy, SortOrder } from "@jellyfin/sdk/lib/generated-client";
import { Button, Dialog, IconButton, Popover, SxProps } from "@mui/material";
import { ArrowDownward, ArrowUpward } from "@mui/icons-material";
import classNames from "classnames";
import "./BackgroundPlaybackPreferences.scss";

interface BackgroundPlaybackPreferencesProps {
onChange: (event: SelectChangeEvent | React.SyntheticEvent) => void;
values: DisplaySettingsValues;
}

export function BackgroundPlaybackPreferences({
onChange,
values,
}: Readonly<BackgroundPlaybackPreferencesProps>) {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const openPopover = Boolean(anchorEl);

const handleClick = useCallback((event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
}, []);

const handleClosePopover = useCallback(() => {
setAnchorEl(null);
}, []);

const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);

// Create wrapper on change callback to toggle the Sort Order state
// when the Icon Button is pressed
const handleSortOrderChange = (
e: SelectChangeEvent | React.SyntheticEvent
) => {
let target = e.currentTarget as HTMLInputElement;
const fieldName = target.name as keyof DisplaySettingsValues;
const fieldValue = target.value;

// Toggle the ascending/descending variable on the event target when the Sort Icon Button is pressed
if (Object.is(values.libraryThemeMediaSortOrder, values?.[fieldName])) {
let newSortOrder = null;
switch (fieldValue) {
case SortOrder.Ascending:
newSortOrder = SortOrder.Descending;
break;
case SortOrder.Descending:
newSortOrder = SortOrder.Ascending;
break;
}

target.value = newSortOrder ?? target.value;
// For an Icon Button, the event target can be either the pressed icon <svg>
// or the <button> element depending on where the click occurs.
// Therefore replace `e.target` with `e.currentTarget`
// which has the updated event value
e.target = target;
}

// Delegate to the supplied onChange function for normal processing
onChange(e);
};

const popoverCssClass = classNames("backgroundPlaybackPopover");
const dialogCssClass = classNames("backgroundPlaybackDialog");
const stackCssClass = classNames("backgroundPlaybackStack");
const themeMediaCheckboxCssClass = classNames(
"checkboxContainer",
"checkboxContainer-withDescription"
);

const themeMediaCheckboxLabelStyleProps: SxProps = {
marginLeft: 0,
};

return (
<div id="background-playback-settings-preferences">
<Typography variant='h2' id="background-playback-settings-header">{globalize.translate('BackgroundPlayback')}</Typography>
<Button onClick={handleClick} id="background-playback-settings-model-button">
{globalize.translate("BackgroundPlayback")}
</Button>
<Popover
id="background-playback-settings-popover"
aria-describedBy="background-playback-settings-popover-description"
className={popoverCssClass}
open={openPopover}
anchorEl={anchorEl}
onClose={handleClosePopover}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'bottom',
horizontal: 'left'
}}
>
{/*<Dialog
open={open}
onClose={handleClose}
className={dialogCssClass}
aria-describedby="background-playback-settings-model-description"
>*/}
<Stack spacing={1} className={stackCssClass}>
<Typography
variant="h3"
id="background-playback-settings-play-theme-media-label"
>
{globalize.translate("ThemeMediaPlayInBackgroundHelp")}
</Typography>
<FormControl
fullWidth
className={themeMediaCheckboxCssClass}
>
<FormControlLabel
aria-describedby="background-playback-settings-lib-backdrops-description"
control={
<Checkbox
checked={values.enableLibraryBackdrops}
onChange={onChange}
/>
}
label={globalize.translate("Backdrops")}
name="enableLibraryBackdrops"
sx={themeMediaCheckboxLabelStyleProps}
/>
<FormControlLabel
aria-describedby="background-playback-settings-lib-theme-songs-description"
control={
<Checkbox
checked={values.enableLibraryThemeSongs}
onChange={onChange}
/>
}
label={globalize.translate("ThemeSongs")}
name="enableLibraryThemeSongs"
sx={themeMediaCheckboxLabelStyleProps}
/>
<FormControlLabel
aria-describedby="background-playback-settings-lib-theme-videos-description"
control={
<Checkbox
checked={values.enableLibraryThemeVideos}
onChange={onChange}
/>
}
label={globalize.translate("ThemeVideos")}
name="enableLibraryThemeVideos"
sx={themeMediaCheckboxLabelStyleProps}
/>
</FormControl>

<Stack
direction={"row"}
spacing={1}
id="background-playback-sort-by-select-stack"
>
<FormControl id="background-playback-sort-by-select-container">
<InputLabel id="background-playback-sort-by-select-label">
{globalize.translate("ThemeMediaSortBy")}
</InputLabel>
<Select
aria-describedby="background-playback-settings-lib-theme-media-sort-by-description"
inputProps={{
name: "libraryThemeMediaSortBy",
}}
labelId="background-playback-sort-by-select-label"
id="background-playback-sort-by-select-dropdown"
onChange={onChange}
value={values.libraryThemeMediaSortBy}
>
<MenuItem value={ItemSortBy.Random}>
{globalize.translate("OptionRandom")}
</MenuItem>
<MenuItem value={ItemSortBy.SortName}>
{globalize.translate("Name")}
</MenuItem>
<MenuItem value={ItemSortBy.Album}>
{globalize.translate("Album")}
</MenuItem>
<MenuItem value={ItemSortBy.AlbumArtist}>
{globalize.translate("AlbumArtist")}
</MenuItem>
<MenuItem value={ItemSortBy.Artist}>
{globalize.translate("Artist")}
</MenuItem>
<MenuItem value={ItemSortBy.DateCreated}>
{globalize.translate("OptionDateAdded")}
</MenuItem>
<MenuItem value={ItemSortBy.DatePlayed}>
{globalize.translate("OptionDatePlayed")}
</MenuItem>
<MenuItem value={ItemSortBy.PlayCount}>
{globalize.translate("OptionPlayCount")}
</MenuItem>
<MenuItem value={ItemSortBy.PremiereDate}>
{globalize.translate("OptionReleaseDate")}
</MenuItem>
<MenuItem value={ItemSortBy.Runtime}>
{globalize.translate("Runtime")}
</MenuItem>
</Select>
</FormControl>
<IconButton
id="background-playback-sort-order-icon-button"
aria-label="sort-order-by"
onClick={handleSortOrderChange}
name="libraryThemeMediaSortOrder"
value={values.libraryThemeMediaSortOrder}
>
{values.libraryThemeMediaSortOrder ==
SortOrder.Ascending && <ArrowUpward />}
{values.libraryThemeMediaSortOrder ==
SortOrder.Descending && <ArrowDownward />}
</IconButton>
</Stack>
</Stack>
{/*</Dialog>*/}
</Popover>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,57 +43,6 @@ export function LibraryPreferences({ onChange, values }: Readonly<LibraryPrefere
</FormHelperText>
</FormControl>

<FormControl fullWidth>
<FormControlLabel
aria-describedby='display-settings-lib-backdrops-description'
control={
<Checkbox
checked={values.enableLibraryBackdrops}
onChange={onChange}
/>
}
label={globalize.translate('Backdrops')}
name='enableLibraryBackdrops'
/>
<FormHelperText id='display-settings-lib-backdrops-description'>
{globalize.translate('EnableBackdropsHelp')}
</FormHelperText>
</FormControl>

<FormControl fullWidth>
<FormControlLabel
aria-describedby='display-settings-lib-theme-songs-description'
control={
<Checkbox
checked={values.enableLibraryThemeSongs}
onChange={onChange}
/>
}
label={globalize.translate('ThemeSongs')}
name='enableLibraryThemeSongs'
/>
<FormHelperText id='display-settings-lib-theme-songs-description'>
{globalize.translate('EnableThemeSongsHelp')}
</FormHelperText>
</FormControl>

<FormControl fullWidth>
<FormControlLabel
aria-describedby='display-settings-lib-theme-videos-description'
control={
<Checkbox
checked={values.enableLibraryThemeVideos}
onChange={onChange}
/>
}
label={globalize.translate('ThemeVideos')}
name='enableLibraryThemeVideos'
/>
<FormHelperText id='display-settings-lib-theme-videos-description'>
{globalize.translate('EnableThemeVideosHelp')}
</FormHelperText>
</FormControl>

<FormControl fullWidth>
<FormControlLabel
aria-describedby='display-settings-show-missing-episodes-description'
Expand Down
5 changes: 5 additions & 0 deletions src/apps/experimental/routes/user/display/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LibraryPreferences } from 'apps/experimental/features/preferences/compo
import { useDisplaySettingForm } from 'apps/experimental/features/preferences/hooks/useDisplaySettingForm';
import { LocalizationPreferences } from 'apps/experimental/features/preferences/components/LocalizationPreferences';
import { NextUpPreferences } from 'apps/experimental/features/preferences/components/NextUpPreferences';
import { BackgroundPlaybackPreferences } from 'apps/experimental/features/preferences/components/BackgroundPlaybackPreferences';
import type { DisplaySettingsValues } from 'apps/experimental/features/preferences/types/displaySettingsValues';
import LoadingComponent from 'components/loading/LoadingComponent';
import Page from 'components/Page';
Expand Down Expand Up @@ -76,6 +77,10 @@ export default function UserDisplayPreferences() {
onChange={handleFieldChange}
values={values}
/>
<BackgroundPlaybackPreferences
onChange={handleFieldChange}
values={values}
/>

<Button
type='submit'
Expand Down
Loading

0 comments on commit a62fbbc

Please sign in to comment.