diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 778d899ef..9765ea68c 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 24ea42c72..6647f6fcb 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 000000000..e802f92ba --- /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 000000000..87c88d8a4 --- /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 458ac7df3..8c6945616 100644 --- a/src/controllers/session/login/index.html +++ b/src/controllers/session/login/index.html @@ -42,6 +42,10 @@ + + + +
+
+ ${QuickConnectDescription} +
+
+ +
+ +
+ diff --git a/src/controllers/user/quickConnect/index.js b/src/controllers/user/quickConnect/index.js new file mode 100644 index 000000000..00fc5488b --- /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 000000000..2b646837c --- /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 04edecf19..10e4a3c9c 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 4bb3eb25d..d2d8e9188 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 f14670d82..868996f60 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 0c08b77db..7e68ff5fe 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.", @@ -507,6 +511,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:", @@ -713,6 +718,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:", @@ -1147,6 +1153,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",