Skip to content

Commit 81ad20d

Browse files
authored
Merge branch 'release-10.10.z' into fix-mediasourceid
2 parents 19cbcb0 + e8e4ff0 commit 81ad20d

File tree

31 files changed

+317
-185
lines changed

31 files changed

+317
-185
lines changed

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jellyfin-web",
3-
"version": "10.10.3",
3+
"version": "10.10.5",
44
"description": "Web interface for Jellyfin",
55
"repository": "https://github.com/jellyfin/jellyfin-web",
66
"license": "GPL-2.0-or-later",

src/apps/dashboard/components/drawer/sections/ServerDrawerSection.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import ListItemButton from '@mui/material/ListItemButton/ListItemButton';
66
import ListItemIcon from '@mui/material/ListItemIcon';
77
import ListItemText from '@mui/material/ListItemText';
88
import ListSubheader from '@mui/material/ListSubheader';
9-
import React, { useCallback, useState } from 'react';
9+
import React, { type MouseEvent, useCallback, useState } from 'react';
1010
import { useLocation } from 'react-router-dom';
1111

1212
import ListItemLink from 'components/ListItemLink';
@@ -32,11 +32,15 @@ const ServerDrawerSection = () => {
3232
const [ isLibrarySectionOpen, setIsLibrarySectionOpen ] = useState(LIBRARY_PATHS.includes(location.pathname));
3333
const [ isPlaybackSectionOpen, setIsPlaybackSectionOpen ] = useState(PLAYBACK_PATHS.includes(location.pathname));
3434

35-
const onLibrarySectionClick = useCallback(() => {
35+
const onLibrarySectionClick = useCallback((e: MouseEvent) => {
36+
e.preventDefault();
37+
e.stopPropagation();
3638
setIsLibrarySectionOpen(isOpen => !isOpen);
3739
}, []);
3840

39-
const onPlaybackSectionClick = useCallback(() => {
41+
const onPlaybackSectionClick = useCallback((e: MouseEvent) => {
42+
e.preventDefault();
43+
e.stopPropagation();
4044
setIsPlaybackSectionOpen(isOpen => !isOpen);
4145
}, []);
4246

src/apps/dashboard/routes/users/parentalcontrol.tsx

+21-9
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const UserParentalControl = () => {
6565
const [ userName, setUserName ] = useState('');
6666
const [ parentalRatings, setParentalRatings ] = useState<ParentalRating[]>([]);
6767
const [ unratedItems, setUnratedItems ] = useState<UnratedNamedItem[]>([]);
68+
const [ maxParentalRating, setMaxParentalRating ] = useState<string>();
6869
const [ accessSchedules, setAccessSchedules ] = useState<AccessSchedule[]>([]);
6970
const [ allowedTags, setAllowedTags ] = useState<string[]>([]);
7071
const [ blockedTags, setBlockedTags ] = useState<string[]>([]);
@@ -163,15 +164,13 @@ const UserParentalControl = () => {
163164
populateRatings(allParentalRatings);
164165

165166
let ratingValue = '';
166-
if (user.Policy?.MaxParentalRating) {
167-
allParentalRatings.forEach(rating => {
168-
if (rating.Value && user.Policy?.MaxParentalRating && user.Policy.MaxParentalRating >= rating.Value) {
169-
ratingValue = `${rating.Value}`;
170-
}
171-
});
172-
}
167+
allParentalRatings.forEach(rating => {
168+
if (rating.Value != null && user.Policy?.MaxParentalRating != null && user.Policy.MaxParentalRating >= rating.Value) {
169+
ratingValue = `${rating.Value}`;
170+
}
171+
});
173172

174-
(page.querySelector('#selectMaxParentalRating') as HTMLSelectElement).value = String(ratingValue);
173+
setMaxParentalRating(ratingValue);
175174

176175
if (user.Policy?.IsAdministrator) {
177176
(page.querySelector('.accessScheduleSection') as HTMLDivElement).classList.add('hide');
@@ -329,11 +328,24 @@ const UserParentalControl = () => {
329328
};
330329
}, [setAllowedTags, setBlockedTags, loadData, userId]);
331330

331+
useEffect(() => {
332+
const page = element.current;
333+
334+
if (!page) {
335+
console.error('[userparentalcontrol] Unexpected null page reference');
336+
return;
337+
}
338+
339+
(page.querySelector('#selectMaxParentalRating') as HTMLSelectElement).value = String(maxParentalRating);
340+
}, [maxParentalRating, parentalRatings]);
341+
332342
const optionMaxParentalRating = () => {
333343
let content = '';
334344
content += '<option value=\'\'></option>';
335345
for (const rating of parentalRatings) {
336-
content += `<option value='${rating.Value}'>${escapeHTML(rating.Name)}</option>`;
346+
if (rating.Value != null) {
347+
content += `<option value='${rating.Value}'>${escapeHTML(rating.Name)}</option>`;
348+
}
337349
}
338350
return content;
339351
};

src/apps/experimental/routes/home.tsx

+24-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import globalize from '../../../lib/globalize';
55
import { clearBackdrop } from '../../../components/backdrop/backdrop';
66
import layoutManager from '../../../components/layoutManager';
77
import Page from '../../../components/Page';
8+
import { EventType } from 'types/eventType';
9+
import Events from 'utils/events';
810

911
import '../../../elements/emby-tabs/emby-tabs';
1012
import '../../../elements/emby-button/emby-button';
@@ -32,6 +34,8 @@ const Home = () => {
3234
const mainTabsManager = useMemo(() => import('../../../components/maintabsmanager'), []);
3335
const tabController = useRef<ControllerProps | null>();
3436
const tabControllers = useMemo<ControllerProps[]>(() => [], []);
37+
38+
const documentRef = useRef<Document>(document);
3539
const element = useRef<HTMLDivElement>(null);
3640

3741
const setTitle = async () => {
@@ -122,25 +126,40 @@ const Home = () => {
122126
} else if (currentTabController?.onResume) {
123127
currentTabController.onResume({});
124128
}
125-
(document.querySelector('.skinHeader') as HTMLDivElement).classList.add('noHomeButtonHeader');
129+
(documentRef.current.querySelector('.skinHeader') as HTMLDivElement).classList.add('noHomeButtonHeader');
126130
}, [ initialTabIndex, mainTabsManager ]);
127131

128132
const onPause = useCallback(() => {
129133
const currentTabController = tabController.current;
130134
if (currentTabController?.onPause) {
131135
currentTabController.onPause();
132136
}
133-
(document.querySelector('.skinHeader') as HTMLDivElement).classList.remove('noHomeButtonHeader');
137+
(documentRef.current.querySelector('.skinHeader') as HTMLDivElement).classList.remove('noHomeButtonHeader');
134138
}, []);
135139

136-
useEffect(() => {
140+
const renderHome = useCallback(() => {
137141
void onSetTabs();
138-
139142
void onResume();
143+
}, [ onResume, onSetTabs ]);
144+
145+
useEffect(() => {
146+
if (documentRef.current?.querySelector('.headerTabs')) {
147+
renderHome();
148+
}
149+
140150
return () => {
141151
onPause();
142152
};
143-
}, [ onPause, onResume, onSetTabs ]);
153+
}, [onPause, renderHome]);
154+
155+
useEffect(() => {
156+
const doc = documentRef.current;
157+
if (doc) Events.on(doc, EventType.HEADER_RENDERED, renderHome);
158+
159+
return () => {
160+
if (doc) Events.off(doc, EventType.HEADER_RENDERED, renderHome);
161+
};
162+
}, [ renderHome ]);
144163

145164
return (
146165
<div ref={element}>

src/components/ConnectionRequired.tsx

+19-15
Original file line numberDiff line numberDiff line change
@@ -150,21 +150,25 @@ const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
150150
useEffect(() => {
151151
// Check connection status on initial page load
152152
const apiClient = ServerConnections.currentApiClient();
153-
const firstConnection = ServerConnections.firstConnection;
154-
console.debug('[ConnectionRequired] connection state', firstConnection?.State);
155-
ServerConnections.firstConnection = null;
156-
157-
if (firstConnection && firstConnection.State !== ConnectionState.SignedIn && !apiClient?.isLoggedIn()) {
158-
handleIncompleteWizard(firstConnection)
159-
.catch(err => {
160-
console.error('[ConnectionRequired] could not start wizard', err);
161-
});
162-
} else {
163-
validateUserAccess()
164-
.catch(err => {
165-
console.error('[ConnectionRequired] could not validate user access', err);
166-
});
167-
}
153+
const connection = Promise.resolve(ServerConnections.firstConnection ? null : ServerConnections.connect());
154+
connection.then(firstConnection => {
155+
console.debug('[ConnectionRequired] connection state', firstConnection?.State);
156+
ServerConnections.firstConnection = true;
157+
158+
if (firstConnection && firstConnection.State !== ConnectionState.SignedIn && !apiClient?.isLoggedIn()) {
159+
handleIncompleteWizard(firstConnection)
160+
.catch(err => {
161+
console.error('[ConnectionRequired] could not start wizard', err);
162+
});
163+
} else {
164+
validateUserAccess()
165+
.catch(err => {
166+
console.error('[ConnectionRequired] could not validate user access', err);
167+
});
168+
}
169+
}).catch(err => {
170+
console.error('[ConnectionRequired] failed to connect', err);
171+
});
168172
}, [handleIncompleteWizard, validateUserAccess]);
169173

170174
if (isLoading) {

src/components/alert.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default async function (text, title) {
77
// Modals seem to be blocked on Web OS and Tizen 2.x
88
const canUseNativeAlert = !!(
99
!browser.web0s
10-
&& !(browser.tizenVersion && browser.tizenVersion < 3)
10+
&& !(browser.tizenVersion && (browser.tizenVersion < 3 || browser.tizenVersion >= 8))
1111
&& browser.tv
1212
&& window.alert
1313
);

src/components/alphaPicker/style.scss

+10-14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@import '../../styles/mixins';
2+
13
.alphaPicker {
24
text-align: center;
35
display: flex;
@@ -11,8 +13,8 @@
1113

1214
.alphaPicker-fixed {
1315
position: fixed;
14-
bottom: 5.5em;
15-
bottom: max(env(safe-area-inset-bottom), 5.5em);
16+
17+
@include conditional-max(bottom, 5.5em, env(safe-area-inset-bottom));
1618
}
1719

1820
.alphaPickerRow {
@@ -45,8 +47,7 @@
4547

4648
@media all and (max-height: 50em) {
4749
.alphaPicker-fixed {
48-
bottom: 5em;
49-
bottom: max(env(safe-area-inset-bottom), 5em);
50+
@include conditional-max(bottom, 5em, env(safe-area-inset-bottom));
5051
}
5152

5253
.alphaPickerButton-vertical {
@@ -105,32 +106,27 @@
105106
}
106107

107108
.alphaPicker-fixed.alphaPicker-tv {
108-
bottom: 1%;
109-
bottom: max(env(safe-area-inset-bottom), 1%);
109+
@include conditional-max(bottom, 1%, env(safe-area-inset-bottom));
110110
}
111111

112112
.alphaPicker-fixed-right {
113113
[dir="ltr"] & {
114-
right: 0.4em;
115-
right: max(env(safe-area-inset-right), 0.4em);
114+
@include conditional-max(right, 0.4em, env(safe-area-inset-right));
116115
}
117116

118117
[dir="rtl"] & {
119-
left: 0.4em;
120-
left: max(env(safe-area-inset-left), 0.4em);
118+
@include conditional-max(left, 0.4em, env(safe-area-inset-left));
121119
}
122120
}
123121

124122
@media all and (min-width: 62.5em) {
125123
.alphaPicker-fixed-right {
126124
[dir="ltr"] & {
127-
right: 1em;
128-
right: max(env(safe-area-inset-right), 1em);
125+
@include conditional-max(right, 1em, env(safe-area-inset-right));
129126
}
130127

131128
[dir="rtl"] & {
132-
left: 1em;
133-
left: max(env(safe-area-inset-left), 1em);
129+
@include conditional-max(left, 1em, env(safe-area-inset-left));
134130
}
135131
}
136132
}

src/components/confirm/confirm.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ function useNativeConfirm() {
77
// webOS seems to block modals
88
// Tizen 2.x seems to block modals
99
return !browser.web0s
10-
&& !(browser.tizenVersion && browser.tizenVersion < 3)
10+
&& !(browser.tizenVersion && (browser.tizenVersion < 3 || browser.tizenVersion >= 8))
1111
&& browser.tv
1212
&& window.confirm;
1313
}

src/components/dialogHelper/dialogHelper.js

+26-24
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function tryRemoveElement(elem) {
4343
}
4444
}
4545

46-
function DialogHashHandler(dlg, hash, resolve, dlgOptions) {
46+
function DialogHashHandler(dlg, hash, resolve) {
4747
const self = this;
4848
self.originalUrl = window.location.href;
4949
const activeElement = document.activeElement;
@@ -158,7 +158,7 @@ function DialogHashHandler(dlg, hash, resolve, dlgOptions) {
158158

159159
dlg.classList.remove('hide');
160160

161-
addBackdropOverlay(dlg, dlgOptions);
161+
addBackdropOverlay(dlg);
162162

163163
dlg.classList.add('opened');
164164
dlg.dispatchEvent(new CustomEvent('open', {
@@ -193,7 +193,7 @@ function DialogHashHandler(dlg, hash, resolve, dlgOptions) {
193193
}
194194
}
195195

196-
function addBackdropOverlay(dlg, dlgOptions = {}) {
196+
function addBackdropOverlay(dlg) {
197197
const backdrop = document.createElement('div');
198198
backdrop.classList.add('dialogBackdrop');
199199

@@ -205,33 +205,35 @@ function addBackdropOverlay(dlg, dlgOptions = {}) {
205205
void backdrop.offsetWidth;
206206
backdrop.classList.add('dialogBackdropOpened');
207207

208-
if (!dlgOptions.preventCloseOnClick) {
209-
dom.addEventListener((dlg.dialogContainer || backdrop), 'click', e => {
210-
if (e.target === dlg.dialogContainer) {
211-
close(dlg);
212-
}
213-
}, {
214-
passive: true
215-
});
216-
}
208+
let clickedElement;
217209

218-
if (!dlgOptions.preventCloseOnRightClick) {
219-
dom.addEventListener((dlg.dialogContainer || backdrop), 'contextmenu', e => {
220-
if (e.target === dlg.dialogContainer) {
221-
// Close the application dialog menu
222-
close(dlg);
223-
// Prevent the default browser context menu from appearing
224-
e.preventDefault();
225-
}
226-
});
227-
}
210+
dom.addEventListener((dlg.dialogContainer || backdrop), 'mousedown', e => {
211+
clickedElement = e.target;
212+
});
213+
214+
dom.addEventListener((dlg.dialogContainer || backdrop), 'click', e => {
215+
if (e.target === dlg.dialogContainer && e.target == clickedElement) {
216+
close(dlg);
217+
}
218+
}, {
219+
passive: true
220+
});
221+
222+
dom.addEventListener((dlg.dialogContainer || backdrop), 'contextmenu', e => {
223+
if (e.target === dlg.dialogContainer) {
224+
// Close the application dialog menu
225+
close(dlg);
226+
// Prevent the default browser context menu from appearing
227+
e.preventDefault();
228+
}
229+
});
228230
}
229231

230232
function isHistoryEnabled(dlg) {
231233
return dlg.getAttribute('data-history') === 'true';
232234
}
233235

234-
export function open(dlg, dlgOptions) {
236+
export function open(dlg) {
235237
if (globalOnOpenCallback) {
236238
globalOnOpenCallback(dlg);
237239
}
@@ -248,7 +250,7 @@ export function open(dlg, dlgOptions) {
248250
document.body.appendChild(dialogContainer);
249251

250252
return new Promise((resolve) => {
251-
new DialogHashHandler(dlg, `dlg${new Date().getTime()}`, resolve, dlgOptions);
253+
new DialogHashHandler(dlg, `dlg${new Date().getTime()}`, resolve);
252254
});
253255
}
254256

0 commit comments

Comments
 (0)