diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 46c40b6c999..b7c64ef9c14 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -34,6 +34,7 @@ - [Ryan Hartzell](https://github.com/ryan-hartzell) - [Thibault Nocchi](https://github.com/ThibaultNocchi) - [MrTimscampi](https://github.com/MrTimscampi) + - [ConfusedPolarBear](https://github.com/ConfusedPolarBear) - [Sarab Singh](https://github.com/sarab97) - [GuilhermeHideki](https://github.com/GuilhermeHideki) - [Andrei Oanca](https://github.com/OancaAndrei) diff --git a/package.json b/package.json index 5ca5c151519..d53ef70cb6e 100644 --- a/package.json +++ b/package.json @@ -166,6 +166,8 @@ "src/components/playmenu.js", "src/components/pluginManager.js", "src/components/prompt/prompt.js", + "src/components/qualityOptions.js", + "src/components/quickConnectSettings/quickConnectSettings.js", "src/components/recordingcreator/recordingbutton.js", "src/components/recordingcreator/recordingcreator.js", "src/components/recordingcreator/seriesrecordingeditor.js", @@ -173,7 +175,6 @@ "src/components/refreshdialog/refreshdialog.js", "src/components/recordingcreator/recordingeditor.js", "src/components/recordingcreator/recordingfields.js", - "src/components/qualityOptions.js", "src/components/remotecontrol/remotecontrol.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", @@ -244,6 +245,7 @@ "src/controllers/dashboard/plugins/installed/index.js", "src/controllers/dashboard/plugins/available/index.js", "src/controllers/dashboard/plugins/repositories/index.js", + "src/controllers/dashboard/quickconnect.js", "src/controllers/dashboard/scheduledtasks/scheduledtask.js", "src/controllers/dashboard/scheduledtasks/scheduledtasks.js", "src/controllers/dashboard/serveractivity.js", @@ -292,6 +294,7 @@ "src/controllers/user/menu/index.js", "src/controllers/user/playback/index.js", "src/controllers/user/profile/index.js", + "src/controllers/user/quickConnect/index.js", "src/controllers/user/subtitles/index.js", "src/controllers/wizard/finish/index.js", "src/controllers/wizard/remote/index.js", diff --git a/src/components/quickConnectSettings/quickConnectSettings.js b/src/components/quickConnectSettings/quickConnectSettings.js new file mode 100644 index 00000000000..e802f92ba13 --- /dev/null +++ b/src/components/quickConnectSettings/quickConnectSettings.js @@ -0,0 +1,41 @@ +import globalize from 'globalize'; +import toast from 'toast'; + +export class QuickConnectSettings { + constructor() { } + + authorize(code) { + let url = ApiClient.getUrl('/QuickConnect/Authorize?Code=' + code); + ApiClient.ajax({ + type: 'POST', + url: url + }, true).then(() => { + toast(globalize.translate('QuickConnectAuthorizeSuccess')); + }).catch(() => { + toast(globalize.translate('QuickConnectAuthorizeFail')); + }); + + // prevent bubbling + return false; + } + + activate() { + let url = ApiClient.getUrl('/QuickConnect/Activate'); + return ApiClient.ajax({ + type: 'POST', + url: url + }).then(() => { + toast(globalize.translate('QuickConnectActivationSuccessful')); + return true; + }).catch((e) => { + console.error('Error activating quick connect. Error:', e); + Dashboard.alert({ + title: globalize.translate('HeaderError'), + message: globalize.translate('DefaultErrorMessage') + }); + throw e; + }); + } +} + +export default QuickConnectSettings; diff --git a/src/controllers/dashboard/quickconnect.js b/src/controllers/dashboard/quickconnect.js new file mode 100644 index 00000000000..87c88d8a41e --- /dev/null +++ b/src/controllers/dashboard/quickconnect.js @@ -0,0 +1,58 @@ +import loading from 'loading'; +import toast from 'toast'; +import globalize from 'globalize'; + +const unavailable = 'Unavailable'; +const available = 'Available'; +const active = 'Active'; +let page; + +export default function(view) { + view.addEventListener('viewshow', function () { + page = this; + loading.show(); + page.querySelector('#btnQuickConnectSubmit').onclick = onSubmit; + updatePage(); + }); +} + +function loadPage(status) { + let check = status === available || status === active; + + page.querySelector('#quickConnectStatus').textContent = status.toLocaleLowerCase(); + page.querySelector('#chkQuickConnectAvailable').checked = check; + + loading.hide(); +} + +function onSubmit() { + loading.show(); + + let newStatus = page.querySelector('#chkQuickConnectAvailable').checked ? available : unavailable; + + let url = ApiClient.getUrl('/QuickConnect/Available?Status=' + newStatus); + + ApiClient.ajax({ + type: 'POST', + url: url + }, true).then(() => { + toast(globalize.translate('SettingsSaved')); + setTimeout(updatePage, 500); + + return true; + }).catch((e) => { + console.error('Unable to set quick connect status. error:', e); + }); + + loading.hide(); + return false; +} + +function updatePage() { + ApiClient.getQuickConnect('Status').then((response) => { + loadPage(response); + return true; + }).catch((e) => { + console.error('Unable to get quick connect status. error:', e); + }); +} diff --git a/src/controllers/session/login/index.html b/src/controllers/session/login/index.html index 458ac7df3be..8c694561685 100644 --- a/src/controllers/session/login/index.html +++ b/src/controllers/session/login/index.html @@ -42,6 +42,10 @@

${HeaderPleaseSignIn}

+ + + +
+
+ ${QuickConnectDescription} +
+
+ +
+ +
+ diff --git a/src/controllers/user/quickConnect/index.js b/src/controllers/user/quickConnect/index.js new file mode 100644 index 00000000000..00fc5488b9d --- /dev/null +++ b/src/controllers/user/quickConnect/index.js @@ -0,0 +1,61 @@ +import QuickConnectSettings from 'quickConnectSettings'; +import globalize from 'globalize'; +import toast from 'toast'; + +export default function (view) { + let quickConnectSettingsInstance = null; + + view.addEventListener('viewshow', function () { + let codeElement = view.querySelector('#txtQuickConnectCode'); + + quickConnectSettingsInstance = new QuickConnectSettings(); + + view.querySelector('#btnQuickConnectActivate').addEventListener('click', () => { + quickConnectSettingsInstance.activate(quickConnectSettingsInstance).then(() => { + renderPage(); + }); + }); + + view.querySelector('#btnQuickConnectAuthorize').addEventListener('click', () => { + if (!codeElement.validity.valid) { + toast(globalize.translate('QuickConnectInvalidCode')); + + return; + } + + let code = codeElement.value; + quickConnectSettingsInstance.authorize(code); + }); + + view.querySelector('.quickConnectSettingsContainer').addEventListener('submit', (e) => { + e.preventDefault(); + }); + + renderPage(); + }); + + function renderPage(forceActive = false) { + ApiClient.getQuickConnect('Status').then((status) => { + let btn = view.querySelector('#btnQuickConnectActivate'); + let container = view.querySelector('.quickConnectSettingsContainer'); + + // The activation button should only be visible when quick connect is unavailable (with the text replaced with an error) or when it is available (so it can be activated) + // The authorization container is only usable when quick connect is active, so it should be hidden otherwise + container.style.display = 'none'; + + if (status === 'Unavailable') { + btn.textContent = globalize.translate('QuickConnectNotAvailable'); + btn.disabled = true; + btn.classList.remove('button-submit'); + btn.classList.add('button'); + } else if (status === 'Active' || forceActive) { + container.style.display = ''; + btn.style.display = 'none'; + } + + return true; + }).catch((e) => { + throw e; + }); + } +} diff --git a/src/quickconnect.html b/src/quickconnect.html new file mode 100644 index 00000000000..2b646837c6b --- /dev/null +++ b/src/quickconnect.html @@ -0,0 +1,24 @@ +
+
+
+
+
+

${QuickConnect}

+
+
+ +
${LabelCurrentStatus}
+ +
+ +
+ + +
+
+
diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index 04edecf1985..10e4a3c9c93 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -404,6 +404,12 @@ import 'flexStyles'; pageIds: ['devicesPage', 'devicePage'], icon: 'devices' }); + links.push({ + name: globalize.translate('QuickConnect'), + href: 'quickConnect.html', + pageIds: ['quickConnectPage'], + icon: 'tap_and_play' + }); links.push({ name: globalize.translate('HeaderActivity'), href: 'serveractivity.html', diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 4bb3eb25d97..d2d8e918866 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -97,6 +97,13 @@ import 'detailtablecss'; controller: 'user/home/index' }); + defineRoute({ + alias: '/mypreferencesquickconnect.html', + path: '/controllers/user/quickConnect/index.html', + autoFocus: false, + transition: 'fade', + controller: 'user/quickConnect/index' + }); defineRoute({ alias: '/mypreferencesplayback.html', path: '/controllers/user/playback/index.html', @@ -151,6 +158,13 @@ import 'detailtablecss'; controller: 'dashboard/devices/device' }); + defineRoute({ + path: '/quickconnect.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/quickconnect' + }); + defineRoute({ alias: '/dlnaprofile.html', path: '/controllers/dashboard/dlna/profile.html', diff --git a/src/scripts/site.js b/src/scripts/site.js index f14670d82dd..868996f602a 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -625,6 +625,7 @@ function initClient() { define('displaySettings', [componentsPath + '/displaySettings/displaySettings'], returnFirstDependency); define('playbackSettings', [componentsPath + '/playbackSettings/playbackSettings'], returnFirstDependency); define('homescreenSettings', [componentsPath + '/homeScreenSettings/homeScreenSettings'], returnFirstDependency); + define('quickConnectSettings', [componentsPath + '/quickConnectSettings/quickConnectSettings'], returnFirstDependency); define('playbackManager', [componentsPath + '/playback/playbackmanager'], getPlaybackManager); define('timeSyncManager', [componentsPath + '/syncPlay/timeSyncManager'], returnDefault); define('groupSelectionMenu', [componentsPath + '/syncPlay/groupSelectionMenu'], returnFirstDependency); diff --git a/src/strings/en-us.json b/src/strings/en-us.json index b3e6a50fbf6..df5b6a36c6f 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -45,6 +45,7 @@ "Audio": "Audio", "AuthProviderHelp": "Select an authentication provider to be used to authenticate this user's password.", "Auto": "Auto", + "Authorize": "Authorize", "Backdrop": "Backdrop", "Backdrops": "Backdrops", "Banner": "Banner", @@ -60,6 +61,7 @@ "Browse": "Browse", "MessageBrowsePluginCatalog": "Browse our plugin catalog to view available plugins.", "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VOBSUB, PGS, SUB, IDX, …) and certain ASS or SSA subtitles.", + "ButtonActivate": "Activate", "ButtonAddImage": "Add Image", "ButtonAddMediaLibrary": "Add Media Library", "ButtonAddScheduledTaskTrigger": "Add Trigger", @@ -107,6 +109,7 @@ "ButtonTogglePlaylist": "Playlist", "ButtonTrailer": "Trailer", "ButtonUninstall": "Uninstall", + "ButtonUseQuickConnect": "Use Quick Connect", "ButtonWebsite": "Website", "CancelRecording": "Cancel recording", "CancelSeries": "Cancel series", @@ -196,6 +199,7 @@ "EnableNextVideoInfoOverlayHelp": "At the end of a video, display info about the next video coming up in the current playlist.", "EnablePhotos": "Display photos", "EnablePhotosHelp": "Images will be detected and displayed alongside other media files.", + "EnableQuickConnect": "Enable quick connect on this server", "EnableStreamLooping": "Auto-loop live streams", "EnableStreamLoopingHelp": "Enable this if live streams only contain a few seconds of data and need to be continuously requested. Enabling this when not needed may cause problems.", "EnableThemeSongsHelp": "Play theme songs in the background while browsing the library.", @@ -506,6 +510,7 @@ "LabelCountry": "Country:", "LabelCriticRating": "Critic rating:", "LabelCurrentPassword": "Current password:", + "LabelCurrentStatus": "Current status:", "LabelCustomCertificatePath": "Custom SSL certificate path:", "LabelCustomCertificatePathHelp": "Path to a PKCS #12 file containing a certificate and private key to enable TLS support on a custom domain.", "LabelCustomCss": "Custom CSS:", @@ -711,6 +716,7 @@ "LabelPublicHttpPortHelp": "The public port number that should be mapped to the local HTTP port.", "LabelPublicHttpsPort": "Public HTTPS port number:", "LabelPublicHttpsPortHelp": "The public port number that should be mapped to the local HTTPS port.", + "LabelQuickConnectCode": "Quick connect code:", "LabelReasonForTranscoding": "Reason for transcoding:", "LabelRecord": "Record:", "LabelRecordingPath": "Default recording path:", @@ -1145,6 +1151,16 @@ "Profile": "Profile", "Programs": "Programs", "Quality": "Quality", + "QuickConnect": "Quick Connect", + "QuickConnectActivationSuccessful": "Successfully activated", + "QuickConnectAuthorizeCode": "Enter code {0} to login", + "QuickConnectAuthorizeSuccess": "Request authorized", + "QuickConnectAuthorizeFail": "Unknown quick connect code", + "QuickConnectDeactivated": "Quick connect was deactivated before the login request could be approved", + "QuickConnectDescription": "To sign in with quick connect, select the Quick Connect button on the device you are logging in from and enter the displayed code below.", + "QuickConnectInvalidCode": "Invalid quick connect code", + "QuickConnectNotAvailable": "Ask your server administrator to enable quick connect", + "QuickConnectNotActive": "Quick connect is not active on this server", "Raised": "Raised", "Rate": "Rate", "RecentlyWatched": "Recently watched",