diff --git a/package.json b/package.json index d6efc241fc..4a23dbcb8a 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "src/components/syncPlay/syncPlayManager.js", "src/components/syncPlay/timeSyncManager.js", "src/controllers/dashboard/logs.js", + "src/controllers/dashboard/plugins/repositories.js", "src/plugins/bookPlayer/plugin.js", "src/plugins/bookPlayer/tableOfContents.js", "src/plugins/photoPlayer/plugin.js", diff --git a/src/assets/css/site.css b/src/assets/css/site.css index d489f77f01..9fbd8a4fca 100644 --- a/src/assets/css/site.css +++ b/src/assets/css/site.css @@ -121,6 +121,11 @@ div[data-role=page] { transform: translateY(-100%); } +.drawerContent { + /* make sure the bottom of the drawer is visible when music is playing */ + padding-bottom: 4em; +} + .force-scroll { overflow-y: scroll; } diff --git a/src/components/dialog/dialog.template.html b/src/components/dialog/dialog.template.html index bee0ef7f73..6d4310c0f4 100644 --- a/src/components/dialog/dialog.template.html +++ b/src/components/dialog/dialog.template.html @@ -4,12 +4,8 @@
- -
- -
+
-
-
+
diff --git a/src/components/formdialog.css b/src/components/formdialog.css index 94695f4865..d7cb162e8c 100644 --- a/src/components/formdialog.css +++ b/src/components/formdialog.css @@ -55,7 +55,7 @@ /* Without this emby-checkbox is able to appear on top */ z-index: 1; align-items: flex-end; - justify-content: flex-end; + justify-content: center; flex-wrap: wrap; } diff --git a/src/components/metadataEditor/personEditor.template.html b/src/components/metadataEditor/personEditor.template.html index 40b29767fa..d2ad6a78d2 100644 --- a/src/components/metadataEditor/personEditor.template.html +++ b/src/components/metadataEditor/personEditor.template.html @@ -7,7 +7,6 @@
-
@@ -23,6 +22,7 @@
+
${LabelPersonRoleHelp}
@@ -33,6 +33,5 @@ ${Save}
- diff --git a/src/components/prompt/prompt.template.html b/src/components/prompt/prompt.template.html index 981fa9f102..a07629ae7e 100644 --- a/src/components/prompt/prompt.template.html +++ b/src/components/prompt/prompt.template.html @@ -2,12 +2,12 @@ +

-
@@ -19,7 +19,6 @@
-
diff --git a/src/controllers/dashboard/plugins/available.js b/src/controllers/dashboard/plugins/available.js index 82fea00b58..37df8801b5 100644 --- a/src/controllers/dashboard/plugins/available.js +++ b/src/controllers/dashboard/plugins/available.js @@ -123,6 +123,9 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby }, { href: 'availableplugins.html', name: globalize.translate('TabCatalog') + }, { + href: 'repositories.html', + name: globalize.translate('TabRepositories') }]; } diff --git a/src/controllers/dashboard/plugins/installed.js b/src/controllers/dashboard/plugins/installed.js index 87e9428cc6..eb521c2fd6 100644 --- a/src/controllers/dashboard/plugins/installed.js +++ b/src/controllers/dashboard/plugins/installed.js @@ -41,7 +41,7 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button' html += '
'; html += '
'; html += '
'; - html += configPageUrl ? '' : ''; @@ -153,6 +153,9 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button' }, { href: 'availableplugins.html', name: globalize.translate('TabCatalog') + }, { + href: 'repositories.html', + name: globalize.translate('TabRepositories') }]; } diff --git a/src/controllers/dashboard/plugins/repositories.js b/src/controllers/dashboard/plugins/repositories.js new file mode 100644 index 0000000000..3087cdd927 --- /dev/null +++ b/src/controllers/dashboard/plugins/repositories.js @@ -0,0 +1,153 @@ +import loading from 'loading'; +import libraryMenu from 'libraryMenu'; +import globalize from 'globalize'; +import dialogHelper from 'dialogHelper'; +import 'emby-button'; +import 'emby-checkbox'; +import 'emby-select'; +import 'formDialogStyle'; +import 'listViewStyle'; + +let repositories = []; + +function reloadList(page) { + loading.show(); + ApiClient.getJSON(ApiClient.getUrl('Repositories')).then(list => { + repositories = list; + populateList({ + listElement: page.querySelector('#repositories'), + noneElement: page.querySelector('#none'), + repositories: repositories + }); + }).catch(error => { + console.error('error loading repositories'); + page.querySelector('#none').classList.remove('hide'); + loading.hide(); + }); +} + +function saveList(page) { + loading.show(); + ApiClient.ajax({ + type: 'POST', + url: ApiClient.getUrl('Repositories'), + data: JSON.stringify(repositories), + contentType: 'application/json' + }).then(response => { + reloadList(page); + }).catch(error => { + console.error('error saving repositories'); + loading.hide(); + }); +} + +function populateList(options) { + var html = ''; + + html += '
'; + for (var i = 0; i < options.repositories.length; i++) { + html += getRepositoryHtml(options.repositories[i]); + } + + html += '
'; + if (!options.repositories.length) { + options.noneElement.classList.remove('hide'); + } + + options.listElement.innerHTML = html; + loading.hide(); +} + +function getRepositoryHtml(repository) { + var html = ''; + + html += '
'; + html += ``; + html += ''; + html += ''; + html += '
'; + html += `

${repository.Name}

`; + html += `
${repository.Url}
`; + html += '
'; + html += ``; + html += '
'; + + return html; +} + +function getTabs() { + return [{ + href: 'installedplugins.html', + name: globalize.translate('TabMyPlugins') + }, { + href: 'availableplugins.html', + name: globalize.translate('TabCatalog') + }, { + href: 'repositories.html', + name: globalize.translate('TabRepositories') + }]; +} + +export default function(view, params) { + view.addEventListener('viewshow', function () { + libraryMenu.setTabs('plugins', 2, getTabs); + reloadList(this); + + var save = this; + $('#repositories', view).on('click', '.btnDelete', function() { + var button = this; + repositories = repositories.filter(function (r) { + return r.Url !== button.id; + }); + + saveList(save); + }); + }); + + view.querySelector('.btnNewRepository').addEventListener('click', () => { + let dialog = dialogHelper.createDialog({ + scrollY: false, + size: 'large', + modal: false, + removeOnClose: true + }); + + let html = ''; + + html += '
'; + html += ''; + html += `

${globalize.translate('HeaderNewRepository')}

`; + html += '
'; + html += '
'; + html += '
'; + html += ``; + html += `
${globalize.translate('LabelRepositoryNameHelp')}
`; + html += '
'; + html += '
'; + html += ``; + html += `
${globalize.translate('LabelRepositoryUrlHelp')}
`; + html += '
'; + html += ``; + html += '
'; + html += ''; + + dialog.innerHTML = html; + dialog.querySelector('.btnCancel').addEventListener('click', () => { + dialogHelper.close(dialog); + }); + + dialog.querySelector('.newPluginForm').addEventListener('submit', () => { + repositories.push({ + Name: dialog.querySelector('#txtRepositoryName').value, + Url: dialog.querySelector('#txtRepositoryUrl').value, + Enabled: true + }); + + saveList(view); + dialogHelper.close(dialog); + return false; + }); + + dialogHelper.open(dialog); + }); +} diff --git a/src/controllers/itemDetails.js b/src/controllers/itemDetails.js index 67aa5f9407..c9b6b7fc1c 100644 --- a/src/controllers/itemDetails.js +++ b/src/controllers/itemDetails.js @@ -400,6 +400,7 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti } else if (item.Album) { parentNameHtml.push(item.Album); } + // FIXME: This whole section needs some refactoring, so it becames easier to scale across all form factors. See GH #1022 var html = ''; var tvShowHtml = parentNameHtml[0]; @@ -415,9 +416,9 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti } } else { if (layoutManager.mobile) { - html = '

' + parentNameHtml.join('
') + '

'; + html = '

' + parentNameHtml.join('
') + '

'; } else { - html = '

' + tvShowHtml + '

'; + html = '

' + tvShowHtml + '

'; } } } @@ -425,20 +426,19 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti var name = itemHelper.getDisplayName(item, { includeParentInfo: false }); - var offset = parentNameLast ? '.25em' : '.5em'; if (html && !parentNameLast) { if (!layoutManager.mobile && tvSeasonHtml) { - html += '

' + tvSeasonHtml + ' - ' + name + '

'; + html += '

' + tvSeasonHtml + ' - ' + name + '

'; } else { - html += '

' + name + '

'; + html += '

' + name + '

'; } } else { - html = '

' + name + '

' + html; + html = '

' + name + '

' + html; } if (item.OriginalTitle && item.OriginalTitle != item.Name) { - html += '

' + item.OriginalTitle + '

'; + html += '

' + item.OriginalTitle + '

'; } container.innerHTML = html; @@ -1106,10 +1106,10 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti var externalLinksElem = page.querySelector('.itemExternalLinks'); renderOverview([overview], item); + var i; var itemMiscInfo; itemMiscInfo = page.querySelectorAll('.itemMiscInfo-primary'); - for (i = 0; i < itemMiscInfo.length; i++) { mediaInfo.fillPrimaryMediaInfo(itemMiscInfo[i], item, { interactive: true, @@ -1125,7 +1125,6 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSetti } itemMiscInfo = page.querySelectorAll('.itemMiscInfo-secondary'); - for (i = 0; i < itemMiscInfo.length; i++) { mediaInfo.fillSecondaryMediaInfo(itemMiscInfo[i], item, { interactive: true diff --git a/src/elements/emby-input/emby-input.js b/src/elements/emby-input/emby-input.js index 03ba2b93aa..1cef349bf0 100644 --- a/src/elements/emby-input/emby-input.js +++ b/src/elements/emby-input/emby-input.js @@ -109,9 +109,7 @@ define(['layoutManager', 'browser', 'dom', 'css!./emby-input', 'registerElement' } EmbyInputPrototype.attachedCallback = function () { - this.labelElement.htmlFor = this.id; - onChange.call(this); }; diff --git a/src/itemdetails.html b/src/itemdetails.html index 18de25845c..44ca561d40 100644 --- a/src/itemdetails.html +++ b/src/itemdetails.html @@ -10,8 +10,8 @@
-
-
+
+
diff --git a/src/repositories.html b/src/repositories.html new file mode 100644 index 0000000000..ff3406fb95 --- /dev/null +++ b/src/repositories.html @@ -0,0 +1,19 @@ +
+
+
+
+

${TabRepositories}

+ +
+ +
+ +
+

${MessageNoRepositories}

+

${MessageAddRepository}

+
+
+
+
diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 6f13711b66..b5bb04683b 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -215,6 +215,12 @@ define([ roles: 'admin', controller: 'dashboard/plugins/available' }); + defineRoute({ + path: '/repositories.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/plugins/repositories' + }); defineRoute({ path: '/home.html', diff --git a/src/strings/en-us.json b/src/strings/en-us.json index b8dfaccaf8..a1ec9d2b23 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1024,6 +1024,13 @@ "MessageItemsAdded": "Items added.", "MessageLeaveEmptyToInherit": "Leave empty to inherit settings from a parent item or the global default value.", "MessageNoAvailablePlugins": "No available plugins.", + "MessageNoRepositories": "No repositories.", + "HeaderNewRepository": "New Repository", + "LabelRepositoryUrl": "Repository URL", + "LabelRepositoryUrlHelp": "The location of the repository manifest you want to include.", + "LabelRepositoryName": "Repository Name", + "LabelRepositoryNameHelp": "A custom name to distinguish this repository from any others added to your server.", + "MessageAddRepository": "If you wish to add a repository, click the button next to the header and fill out the requested information.", "MessageNoCollectionsAvailable": "Collections allow you to enjoy personalized groupings of Movies, Series, and Albums. Click the + button to start creating collections.", "MessageNoGenresAvailable": "Enable some metadata providers to pull genres from the internet.", "MessageNoMovieSuggestionsAvailable": "No movie suggestions are currently available. Start watching and rating your movies, and then come back to view your recommendations.", @@ -1415,6 +1422,7 @@ "TabAlbums": "Albums", "TabArtists": "Artists", "TabCatalog": "Catalog", + "TabRepositories": "Repositories", "TabChannels": "Channels", "TabCodecs": "Codecs", "TabCollections": "Collections",