1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Fix type errors in playlist editor

This commit is contained in:
Bill Thornton 2024-04-13 02:26:41 -04:00
parent d3c343be4a
commit fa16daabb0
3 changed files with 110 additions and 69 deletions

1
src/apiclient.d.ts vendored
View file

@ -76,6 +76,7 @@ declare module 'jellyfin-apiclient' {
accessToken(): string; accessToken(): string;
addMediaPath(virtualFolderName: string, mediaPath: string, networkSharePath: string, refreshLibrary?: boolean): Promise<void>; addMediaPath(virtualFolderName: string, mediaPath: string, networkSharePath: string, refreshLibrary?: boolean): Promise<void>;
addVirtualFolder(name: string, type?: string, refreshLibrary?: boolean, libraryOptions?: any): Promise<void>; addVirtualFolder(name: string, type?: string, refreshLibrary?: boolean, libraryOptions?: any): Promise<void>;
ajax(request: any): Promise<any>;
appName(): string; appName(): string;
appVersion(): string; appVersion(): string;
authenticateUserByName(name: string, password: string): Promise<AuthenticationResult>; authenticateUserByName(name: string, password: string): Promise<AuthenticationResult>;

View file

@ -1,53 +1,77 @@
import escapeHtml from 'escape-html'; import escapeHtml from 'escape-html';
import dom from '../../scripts/dom'; import type { ApiClient } from 'jellyfin-apiclient';
import dom from 'scripts/dom';
import globalize from 'scripts/globalize';
import * as userSettings from 'scripts/settings/userSettings';
import { PluginType } from 'types/plugin';
import dialogHelper from '../dialogHelper/dialogHelper'; import dialogHelper from '../dialogHelper/dialogHelper';
import loading from '../loading/loading'; import loading from '../loading/loading';
import layoutManager from '../layoutManager'; import layoutManager from '../layoutManager';
import { playbackManager } from '../playback/playbackmanager'; import { playbackManager } from '../playback/playbackmanager';
import { pluginManager } from '../pluginManager'; import { pluginManager } from '../pluginManager';
import * as userSettings from '../../scripts/settings/userSettings';
import { appRouter } from '../router/appRouter'; import { appRouter } from '../router/appRouter';
import globalize from '../../scripts/globalize';
import { PluginType } from '../../types/plugin';
import '../../elements/emby-button/emby-button';
import '../../elements/emby-input/emby-input';
import '../../elements/emby-button/paper-icon-button-light';
import '../../elements/emby-select/emby-select';
import 'material-design-icons-iconfont';
import '../formdialog.scss';
import ServerConnections from '../ServerConnections'; import ServerConnections from '../ServerConnections';
let currentServerId; import 'elements/emby-button/emby-button';
import 'elements/emby-input/emby-input';
import 'elements/emby-button/paper-icon-button-light';
import 'elements/emby-select/emby-select';
function onSubmit(e) { import 'material-design-icons-iconfont';
const panel = dom.parentWithClass(this, 'dialog'); import '../formdialog.scss';
const playlistId = panel.querySelector('#selectPlaylistToAddTo').value; interface DialogElement extends HTMLDivElement {
submitted?: boolean
}
interface PlaylistEditorOptions {
items: string[],
serverId: string,
enableAddToPlayQueue?: boolean,
defaultValue?: string
}
let currentServerId: string;
function onSubmit(this: HTMLElement, e: Event) {
const panel = dom.parentWithClass(this, 'dialog') as DialogElement | null;
if (panel) {
const playlistId = panel.querySelector<HTMLSelectElement>('#selectPlaylistToAddTo')?.value;
const apiClient = ServerConnections.getApiClient(currentServerId); const apiClient = ServerConnections.getApiClient(currentServerId);
if (playlistId) { if (playlistId) {
userSettings.set('playlisteditor-lastplaylistid', playlistId); userSettings.set('playlisteditor-lastplaylistid', playlistId);
addToPlaylist(apiClient, panel, playlistId); addToPlaylist(apiClient, panel, playlistId)
?.catch(err => {
console.error('[PlaylistEditor] Failed to add to playlist %s', playlistId, err);
});
} else { } else {
createPlaylist(apiClient, panel); createPlaylist(apiClient, panel)
?.catch(err => {
console.error('[PlaylistEditor] Failed to create playlist', err);
});
}
} else {
console.error('[PlaylistEditor] Dialog element is missing!');
} }
e.preventDefault(); e.preventDefault();
return false; return false;
} }
function createPlaylist(apiClient, dlg) { function createPlaylist(apiClient: ApiClient, dlg: DialogElement) {
loading.show(); loading.show();
const url = apiClient.getUrl('Playlists', { const url = apiClient.getUrl('Playlists', {
Name: dlg.querySelector('#txtNewPlaylistName').value, Name: dlg.querySelector<HTMLInputElement>('#txtNewPlaylistName')?.value,
Ids: dlg.querySelector('.fldSelectedItemIds').value || '', Ids: dlg.querySelector<HTMLInputElement>('.fldSelectedItemIds')?.value || '',
userId: apiClient.getCurrentUserId() userId: apiClient.getCurrentUserId()
}); });
apiClient.ajax({ return apiClient.ajax({
type: 'POST', type: 'POST',
url: url, url: url,
dataType: 'json', dataType: 'json',
@ -62,12 +86,12 @@ function createPlaylist(apiClient, dlg) {
}); });
} }
function redirectToPlaylist(apiClient, id) { function redirectToPlaylist(apiClient: ApiClient, id: string) {
appRouter.showItem(id, apiClient.serverId()); appRouter.showItem(id, apiClient.serverId());
} }
function addToPlaylist(apiClient, dlg, id) { function addToPlaylist(apiClient: ApiClient, dlg: DialogElement, id: string) {
const itemIds = dlg.querySelector('.fldSelectedItemIds').value || ''; const itemIds = dlg.querySelector<HTMLInputElement>('.fldSelectedItemIds')?.value || '';
if (id === 'queue') { if (id === 'queue') {
playbackManager.queue({ playbackManager.queue({
@ -86,7 +110,7 @@ function addToPlaylist(apiClient, dlg, id) {
userId: apiClient.getCurrentUserId() userId: apiClient.getCurrentUserId()
}); });
apiClient.ajax({ return apiClient.ajax({
type: 'POST', type: 'POST',
url: url url: url
@ -98,16 +122,20 @@ function addToPlaylist(apiClient, dlg, id) {
}); });
} }
function triggerChange(select) { function triggerChange(select: HTMLSelectElement) {
select.dispatchEvent(new CustomEvent('change', {})); select.dispatchEvent(new CustomEvent('change', {}));
} }
function populatePlaylists(editorOptions, panel) { function populatePlaylists(editorOptions: PlaylistEditorOptions, panel: DialogElement) {
const select = panel.querySelector('#selectPlaylistToAddTo'); const select = panel.querySelector<HTMLSelectElement>('#selectPlaylistToAddTo');
loading.hide(); loading.hide();
panel.querySelector('.newPlaylistInfo').classList.add('hide'); if (!select) {
return Promise.reject(new Error('Playlist <select> element is missing'));
}
panel.querySelector('.newPlaylistInfo')?.classList.add('hide');
const options = { const options = {
Recursive: true, Recursive: true,
@ -119,7 +147,7 @@ function populatePlaylists(editorOptions, panel) {
const apiClient = ServerConnections.getApiClient(currentServerId); const apiClient = ServerConnections.getApiClient(currentServerId);
const SyncPlay = pluginManager.firstOfType(PluginType.SyncPlay)?.instance; const SyncPlay = pluginManager.firstOfType(PluginType.SyncPlay)?.instance;
apiClient.getItems(apiClient.getCurrentUserId(), options).then(result => { return apiClient.getItems(apiClient.getCurrentUserId(), options).then(result => {
let html = ''; let html = '';
if ((editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) || SyncPlay?.Manager.isSyncPlayEnabled()) { if ((editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) || SyncPlay?.Manager.isSyncPlayEnabled()) {
@ -128,7 +156,7 @@ function populatePlaylists(editorOptions, panel) {
html += `<option value="">${globalize.translate('OptionNew')}</option>`; html += `<option value="">${globalize.translate('OptionNew')}</option>`;
html += result.Items.map(i => { html += result.Items?.map(i => {
return `<option value="${i.Id}">${escapeHtml(i.Name)}</option>`; return `<option value="${i.Id}">${escapeHtml(i.Name)}</option>`;
}); });
@ -151,7 +179,7 @@ function populatePlaylists(editorOptions, panel) {
}); });
} }
function getEditorHtml(items) { function getEditorHtml(items: string[]) {
let html = ''; let html = '';
html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">'; html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">';
@ -186,58 +214,70 @@ function getEditorHtml(items) {
return html; return html;
} }
function initEditor(content, options, items) { function initEditor(content: DialogElement, options: PlaylistEditorOptions, items: string[]) {
content.querySelector('#selectPlaylistToAddTo').addEventListener('change', function () { content.querySelector('#selectPlaylistToAddTo')?.addEventListener('change', function(this: HTMLSelectElement) {
if (this.value) { if (this.value) {
content.querySelector('.newPlaylistInfo').classList.add('hide'); content.querySelector('.newPlaylistInfo')?.classList.add('hide');
content.querySelector('#txtNewPlaylistName').removeAttribute('required'); content.querySelector('#txtNewPlaylistName')?.removeAttribute('required');
} else { } else {
content.querySelector('.newPlaylistInfo').classList.remove('hide'); content.querySelector('.newPlaylistInfo')?.classList.remove('hide');
content.querySelector('#txtNewPlaylistName').setAttribute('required', 'required'); content.querySelector('#txtNewPlaylistName')?.setAttribute('required', 'required');
} }
}); });
content.querySelector('form').addEventListener('submit', onSubmit); content.querySelector('form')?.addEventListener('submit', onSubmit);
content.querySelector('.fldSelectedItemIds', content).value = items.join(','); const selectedItemsInput = content.querySelector<HTMLInputElement>('.fldSelectedItemIds');
if (selectedItemsInput) {
selectedItemsInput.value = items.join(',');
}
if (items.length) { if (items.length) {
content.querySelector('.fldSelectPlaylist').classList.remove('hide'); content.querySelector('.fldSelectPlaylist')?.classList.remove('hide');
populatePlaylists(options, content); populatePlaylists(options, content)
.catch(err => {
console.error('[PlaylistEditor] failed to populate playlists', err);
});
} else { } else {
content.querySelector('.fldSelectPlaylist').classList.add('hide'); content.querySelector('.fldSelectPlaylist')?.classList.add('hide');
const selectPlaylistToAddTo = content.querySelector('#selectPlaylistToAddTo'); const selectPlaylistToAddTo = content.querySelector<HTMLSelectElement>('#selectPlaylistToAddTo');
if (selectPlaylistToAddTo) {
selectPlaylistToAddTo.innerHTML = ''; selectPlaylistToAddTo.innerHTML = '';
selectPlaylistToAddTo.value = ''; selectPlaylistToAddTo.value = '';
triggerChange(selectPlaylistToAddTo); triggerChange(selectPlaylistToAddTo);
} }
}
} }
function centerFocus(elem, horiz, on) { function centerFocus(elem: HTMLDivElement | null, horiz: boolean, on: boolean) {
import('../../scripts/scrollHelper').then((scrollHelper) => { if (!elem) {
console.error('[PlaylistEditor] cannot focus null element');
return;
}
import('../../scripts/scrollHelper')
.then((scrollHelper) => {
const fn = on ? 'on' : 'off'; const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz); scrollHelper.centerFocus[fn](elem, horiz);
})
.catch(err => {
console.error('[PlaylistEditor] failed to load scroll helper', err);
}); });
} }
export class PlaylistEditor { export class PlaylistEditor {
show(options) { show(options: PlaylistEditorOptions) {
const items = options.items || {}; const items = options.items || [];
currentServerId = options.serverId; currentServerId = options.serverId;
const dialogOptions = { const dialogOptions = {
removeOnClose: true, removeOnClose: true,
scrollY: false scrollY: false,
size: layoutManager.tv ? 'fullscreen' : 'small'
}; };
if (layoutManager.tv) { const dlg: DialogElement = dialogHelper.createDialog(dialogOptions);
dialogOptions.size = 'fullscreen';
} else {
dialogOptions.size = 'small';
}
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog'); dlg.classList.add('formDialog');
@ -258,7 +298,7 @@ export class PlaylistEditor {
initEditor(dlg, options, items); initEditor(dlg, options, items);
dlg.querySelector('.btnCancel').addEventListener('click', () => { dlg.querySelector('.btnCancel')?.addEventListener('click', () => {
dialogHelper.close(dlg); dialogHelper.close(dlg);
}); });

View file

@ -68,7 +68,7 @@ export class UserSettings {
* Set value of setting. * Set value of setting.
* @param {string} name - Name of setting. * @param {string} name - Name of setting.
* @param {mixed} value - Value of setting. * @param {mixed} value - Value of setting.
* @param {boolean} enableOnServer - Flag to save preferences on server. * @param {boolean} [enableOnServer] - Flag to save preferences on server.
*/ */
set(name, value, enableOnServer) { set(name, value, enableOnServer) {
const userId = this.currentUserId; const userId = this.currentUserId;
@ -90,7 +90,7 @@ export class UserSettings {
/** /**
* Get value of setting. * Get value of setting.
* @param {string} name - Name of setting. * @param {string} name - Name of setting.
* @param {boolean} enableOnServer - Flag to return preferences from server (cached). * @param {boolean} [enableOnServer] - Flag to return preferences from server (cached).
* @return {string} Value of setting. * @return {string} Value of setting.
*/ */
get(name, enableOnServer) { get(name, enableOnServer) {